Boost :: Spirit :: Карма альтернативный выбор на основе свойств ввода

Я пытаюсь написать генератор boost :: spirit :: karma, где некоторые выходные данные зависят от нетривиальных свойств входных значений.

Фактическая проблема является частью большой грамматики, но этот пример имеет те же свойства, что и некоторые другие проблемные правила, и фактически является одним из правил грамматики, которые вызывают у меня проблемы.

Я начну с минимального примера, который почти то, что я хочу, а затем поработаю.

#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/home/phoenix.hpp>
#include <boost/fusion/adapted.hpp>
#include <string>
#include <vector>

template<typename OutputIterator_T>
struct Test_Grammar :
boost::spirit::karma::grammar<OutputIterator_T, std::vector<double>()>
{
Test_Grammar() : Test_Grammar::base_type(start), start(), value()
{
namespace karma = boost::spirit::karma;

start
= *(value % karma::lit(", "))
;

value
= (karma::double_)
;
}

boost::spirit::karma::rule<OutputIterator_T, std::vector<double>()> start;
boost::spirit::karma::rule<OutputIterator_T, double()> value;
};

template <typename OutputIterator_T>
bool generate_output(OutputIterator_T& sink, std::vector<double> const& data)
{
Test_Grammar<OutputIterator_T> grammar;
return (boost::spirit::karma::generate(sink, grammar, data));
}

int main (int, char**)
{
std::string generated;
std::back_insert_iterator<std::string> sink(generated);

std::vector<double> data{1.5, 0.0, -2.5,
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::infinity()};

generate_output(sink, data);

std::cout << generated << std::endl;

return 0;
}

Приведенный выше код определяет грамматику, которая при подаче тестовых данных выдает результат

1.5, 0.0, -2.5, nan, inf

Тем не менее, вывод, который я хочу

1.5, 0.0, -2.5, special, special

Если я заменю value часть грамматики с

  value
= (&karma::double_(std::numeric_limits<double>::quiet_NaN()) <<
karma::lit("special"))
| (&karma::double_(std::numeric_limits<double>::infinity()) <<
karma::lit("special"))
| (karma::double_)
;

Я получаю желаемое поведение на бесконечность. Тем не менее, я не получаю желаемый результат для NaN, так как NaN обладает свойством (NaN! = NaN) в сравнениях. Поэтому мне нужен способ использовать макросы / функции fpclassify, такие как isfinite ().

Я должен быть в состоянии получить то, что я хочу, заменив value часть грамматики с

  value
= (karma::eps(...) << karma::lit("special"))
| (karma::double_)
;

Тем не менее, каждая комбинация вызовов функций, указателей функций и заклинаний связывания, которые я пробовал для ... часть привела к ошибкам компилятора.

Любая помощь приветствуется.

ОБНОВИТЬ:

Сехе предоставил отличное общее решение (которое я принял). Спасибо!

Для моего конкретного случая использования я смог еще больше упростить ответ sehe и хотел задокументировать это здесь для других.

После изменения всех включений от <boost/spirit/home/*> в <boost/spirit/include/*> и определяя BOOST_SPIRIT_USE_PHOENIX_V3 прежде чем включить, я добавил следующую строку

BOOST_PHOENIX_ADAPT_FUNCTION(bool, isfinite_, std::isfinite, 1)

и изменил value часть грамматики к этому

  value
%= karma::double_[karma::_pass = isfinite_(karma::_1)]
|  karma::lit("special")
;

1

Решение

Я бы использовал семантическое действие для динамического «провала» double_ генератор:

  value
%= karma::double_ [ karma::_pass = !(isnan_(karma::_1) || isinf_(karma::_1)) ]
| karma::lit("special")
;

Теперь, как мы получаем isnan_ а также isinf_ реализованы? Я предпочитаю использовать Phoenix V3 (который будет использоваться по умолчанию во всех следующих выпусках Boost):

BOOST_PHOENIX_ADAPT_FUNCTION(bool, isnan_, std::isnan, 1)
BOOST_PHOENIX_ADAPT_FUNCTION(bool, isinf_, std::isinf, 1)

Это все. Видеть это Жить на Колиру

Заметки

  • использование %= чтобы получить автоматическое распространение атрибута, даже если есть семантическое действие
  • включают include/*.hpp вместо home/*.hpp

Полный список:

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/fusion/adapted.hpp>
#include <string>
#include <vector>
#include <cmath>

BOOST_PHOENIX_ADAPT_FUNCTION(bool, isnan_, std::isnan, 1)
BOOST_PHOENIX_ADAPT_FUNCTION(bool, isinf_, std::isinf, 1)

template<typename OutputIterator_T>
struct Test_Grammar :
boost::spirit::karma::grammar<OutputIterator_T, std::vector<double>()>
{
Test_Grammar() : Test_Grammar::base_type(start), start(), value()
{
namespace karma = boost::spirit::karma;
namespace phx = boost::phoenix;

start
= *(value % karma::lit(", "))
;

value
%= karma::double_ [ karma::_pass = !(isnan_(karma::_1) || isinf_(karma::_1)) ]
| karma::lit("special")
;
}

boost::spirit::karma::rule<OutputIterator_T, std::vector<double>()> start;
boost::spirit::karma::rule<OutputIterator_T, double()> value;
};

template <typename OutputIterator_T>
bool generate_output(OutputIterator_T& sink, std::vector<double> const& data)
{
Test_Grammar<OutputIterator_T> grammar;
return (boost::spirit::karma::generate(sink, grammar, data));
}

int main (int, char**)
{
std::string generated;
std::back_insert_iterator<std::string> sink(generated);

std::vector<double> data{1.5, 0.0, -2.5,
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::infinity()};

generate_output(sink, data);

std::cout << generated << std::endl;

return 0;
}

Выход

1.5, 0.0, -2.5, special, special
2

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