提升::精神::因果报应基于输入属性的替代选择
boost::spirit::karma alternative selection based on properties of the input
我正试图编写一个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
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 2D数组来自文本输入,中间有空格
- 如何使用 < 和 > 命令获取 c++ 中的输入和输出?
- 检查输入是否不是整数或数字
- 正在尝试了解输入验证循环
- 读取文件并输入到矢量中
- C++如何通过用户输入删除列表元素
- 用c++从输入文件中读取另一行
- 读取文件的最后一行并输入到链接列表时出错
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- 如何使用用户输入在C++中正确填充2D数组
- C++MySQL C api用户输入行
- 输入到文件并输出到另一个文件,并将流文件传递给函数
- 用户定义函数中的指针和输入
- C++概念:如何使用'concept'检查模板化结构的属性?
- 如何在C++中检查2D数组中负值的输入验证
- 程序在没有输入的情况下退出,它有基本的,驱动的类属性初始化问题
- 重载>>运算符未在输入上设置属性
- 提升::精神::因果报应基于输入属性的替代选择