Повысить карму — не потребляющий предикат

Мне нужно напечатать std :: complex, но пропуская мнимую часть, если она равна нулю. Итак, у меня есть правило с двумя постановками:

karma::rule<OutputIterator, std::complex<double>()> complexRule =
'(' << double_ << ", " double_ << ')'
| double_ << omit[double_];

Таким образом, Карма всегда выберет первое производство, поэтому мне нужен какой-то предикат, который примет решение. Учебник «Повысьте карму» поставляется с тем решением, которое требует адаптации std :: complex как кортежа из трех элементов.

BOOST_FUSION_ADAPT_ADT(
std::complex<double>,
(bool, bool, obj.imag() != 0, /**/)
(double, double, obj.real(), /**/)
(double, double, obj.imag(), /**/)
)

но, к сожалению, я не могу этого сделать, так как другой код использует std :: complex, адаптированный как кортеж из двух элементов. Есть ли способ решить эту проблему, не добавляя предикат прямо в адаптер Fusion?

Я пытался использовать карма :: генератор eps в качестве предиката

auto rule = eps( ... ) << '(' << double_ << ", " << double_ << ')'
| double_ << omit[double_];

но я не знаю, какое выражение Phoenix я должен поместить в eps (…), и так как Epsilon Generator не использует какой-либо атрибут, я не уверен, возможно ли получить доступ к std :: complex из него?

2

Решение

Лично я бы держался подальше от адаптации этого как последовательности (я не уверен, как вы приспособили это как последовательность слияния двух элементов во-первых).

Однако это сделано, это не будет универсальным (так что вам придется использовать отдельные адаптации для аргументов разных типов (float, double, long double, boost::multiprecision::number<boost::multiprecision::cpp_dec_float<50>> так далее.).

Это похоже на работу для Духа точки настройки:

namespace boost { namespace spirit { namespace traits {

template <typename T>
struct extract_from_attribute<typename std::complex<T>, boost::fusion::vector2<T, T>, void>
{
typedef boost::fusion::vector2<T,T> type;

template <typename Context>
static type call(std::complex<T> const& attr, Context& context)
{
return { attr.real(), attr.imag() };
}
};

} } }

Теперь вы можете просто использовать любой std::complex<T> с правилом / выражением, ожидающим последовательность слияния:

 rule =
'(' << karma::double_ << ", " << karma::duplicate [ !karma::double_(0.0) << karma::double_ ] << ')'
| karma::double_ << karma::omit [ karma::double_ ];

Обратите внимание, как

  • я использовал duplicate[] в тестовое задание за 0.0 перед испусканием выхода
  • На другой ветке я использовал omit потреблять мнимую часть, ничего не показывая

Вот полная демонстрация, Жить на Колиру

#include <boost/spirit/include/karma.hpp>
#include <complex>

namespace boost { namespace spirit { namespace traits {

template <typename T>
struct extract_from_attribute<typename std::complex<T>, boost::fusion::vector2<T, T>, void>
{
typedef boost::fusion::vector2<T,T> type;

template <typename Context>
static type call(std::complex<T> const& attr, Context& context)
{
return { attr.real(), attr.imag() };
}
};

} } }

namespace karma = boost::spirit::karma;

int main()
{
karma::rule<boost::spirit::ostream_iterator, boost::fusion::vector2<double, double>()>
static const rule =
'(' << karma::double_ << ", " << karma::duplicate [ !karma::double_(0.0) << karma::double_ ] << ')'
| karma::double_ << karma::omit [ karma::double_ ];

std::vector<std::complex<double>> const values {
{ 123, 4 },
{ 123, 0 },
{ 123, std::numeric_limits<double>::infinity() },
{ std::numeric_limits<double>::quiet_NaN(), 0 },
{ 123, -1 },
};

std::cout << karma::format_delimited(*rule, '\n', values);
}

Выход:

(123.0, 4.0)
123.0
(123.0, inf)
nan
(123.0, -1.0)
3

Другие решения

Других решений пока нет …