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