До сих пор мы работали над тем, что<std::complex<>
>не является родным.Boost.FusionПоследовательность. Мы не смогли использовать его с той же простотой и естественной изяществом<fusion::tuple<>
>или аналогичного.Структура данных. К счастью, начиная с Boost V1.43 можно адаптировать любую структуру данных (не только, как раньше, структуры с общедоступными членами) какBoost. Последовательность. Все, что нам нужно сделать, это использовать один из новых макросов<BOOST_FUSION_ADAPT_ADT
>.
Давайте начнем с кода снова, а затем с объяснений.
Не было бы оптимальным, если бы мы могли передать наш пример<std::complex<>
>непосредственно.Функция Кармы<generate()
>:
template <typename OutputIterator>
bool generate_complex(OutputIterator sink, std::complex<double> const& c)
{
using boost::spirit::karma::double_;
using boost::spirit::karma::bool_;
using boost::spirit::karma::true_;
using boost::spirit::karma::omit;
using boost::spirit::karma::generate;
return generate(sink,
(
&true_ << '(' << double_ << ", " << double_ << ')'
| omit[bool_] << double_
),
c
);
}
Действительно, это возможно! Все, что нам нужно, чтобы сделать эту работу, это магическое заклинание (где-то в глобальном пространстве имен):
BOOST_FUSION_ADAPT_ADT(
std::complex<double>,
(bool, bool, obj.imag() != 0, )
(double, double, obj.real(), )
(double, double, obj.imag(), )
)
Большая часть самой грамматики форматирования не изменилась с последнего раздела. Мы используем очень похожую схему. У нас есть альтернатива, предоставляющая правила форматирования для обоих вариантов использования: один для полного сложного формата и один для сложных чисел с нулевой воображаемой частью. Но вместо того, чтобы выбрать необходимую альтернативу, сравнив воображаемую часть с нулем в грамматике, мы предполагаем получить булев атрибут, несущий эту информацию:
&true_ << "(" << double_ << ", " << double_ << ")"
Это означает: «Если первый (булевый) элемент поставляемой последовательности синтеза<true
>, продолжайте, как указано, иначе выберите следующую альтернативу». Следующая альтернатива теперь относится и к булевому элементу, но в остальном (почти) не изменилась.
Теперь должно быть понятно, почему наша адаптивная конструкция выше раскрывает три элементапоследовательности Boost.Fusion: булевое и два двойных значения (реальная и мнимая часть комплексного числа). Мы хотим, чтобы он соответствовал требованиям нашей грамматики форматирования, которая ожидает этих точных значений. Макрос<BOOST_FUSION_ADAPT_ADT
>позволяет нам указать конструкцию произвольного аксессуара, не обязательно ограничиваясь просто вызовом функции-члена экземпляра объекта (представленного<obj
>в контексте этого макроса). Это позволяет нам красиво инкапсулировать логику принятия решений в адаптацию класса.
Вот последний новый фрагмент информации. Если вы посмотрите внимательно, вы поймете, что вторая альтернатива «более короткая», чем первая. Он потребляет только два элемента поставляемой последовательности синтеза: он игнорирует булев и использует реальную часть комплексного числа для генерации своего выхода. Если в нашем атрибуте больше элементов, чем необходимо, мы теперь можем смело опустить их из грамматики (которая является новой «особенностью», добавленной кДухув V1.43). Мы могли бы написать альтернативу как
&false_ << double_
Но это было бы немного менее эффективно, поскольку нам нужно было снова сравнить значение булева, в то время как окончательное решение будет просто игнорировать его.