提升::精神::因果报应基于输入属性的替代选择

boost::spirit::karma alternative selection based on properties of the input

本文关键字:输入 属性 替代选择 精神 因果报应 于输入 提升      更新时间:2023-10-16

我正试图编写一个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所需的结果。因此,我需要一种方法来使用fpclassification宏/函数,如isfinite()。

我应该能够通过用替换语法的value部分来获得我想要的内容

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

然而,我为...部分尝试的函数调用、函数指针和绑定咒语的每一种组合都会导致编译器错误。

任何帮助都将不胜感激。

更新:

Sehe提供了一个出色的通用解决方案(我已经接受了)。非常感谢。

对于我的特定用例,我能够进一步简化sehe的回答,并希望在这里为其他人记录下来。

在将所有includes从<boost/spirit/home/*>更改为<boost/spirit/include/*>并在includes之前定义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")
    ;

我会使用语义操作来动态"失败"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)

仅此而已。查看Coliru直播

票据

  • 使用%=可以获得自动属性传播,即使存在语义操作
  • 包括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