c++ - boost::spirit::karma 基于输入属性的替代选择

标签 c++ boost boost-spirit boost-spirit-karma

我正在尝试编写一个 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_)
    ;

但是,我为 ... 尝试过的函数调用、函数指针和绑定(bind)咒语的每种组合部分导致了编译器错误。

任何帮助将不胜感激。

更新:

Sehe 提供了一个优秀的通用解决方案(我已接受)。谢谢!

对于我的特定用例,我能够进一步简化 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")
    ;

最佳答案

我会使用语义操作动态地使 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)

仅此而已。看到它 Live On 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

关于c++ - boost::spirit::karma 基于输入属性的替代选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23260877/

相关文章:

c++ - 使用已删除的 shared_ptr 中的原始指针的未定义行为?

C++将函数模板作为参数传递给其他函数作为回调

c++ - unordered_map 不起作用

c++ - Boost spirit语法匹配嘈杂的输入

c++ - 使用 std::vector 成员的 std::vector 为类实现 move 语义的正确方法

c++ - 将 0A 替换为\n

c++ - 变量隐藏在 Boost Phoenix 的嵌套 let block 中

boost - 如何在 Boost 中从 http::response 读取 header

c++ - 在没有船长的情况下提升精神解析

c++ - 如何使用spirit解析器获取错误位置