我一直在学习boost :: spirit,并且在语法构造过程中对语义动作进行评估时遇到了困惑。以下代码生成输出:
string=
我的假设是此输出作为附加到orule
的语义操作的一部分。
有没有办法避免这种行为?或者,如果我在语义行为中使用std::cout
,我是否需要与之合作?
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <string>
namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct my_grammar : qi::grammar<Iterator, std::string( ) >
{
my_grammar() : my_grammar::base_type(orule)
{
using qi::_1;
using qi::_2;
using qi::attr;
using qi::string;
using phx::val;
orule = string("abc") [ std::cout << "string=" << _1 << std::endl ];
}
qi::rule< Iterator, std::string() > orule;
};
int main()
{
typedef std::string::const_iterator iterator_type;
typedef my_grammar<iterator_type> parser;
parser my_parser; // Our grammar
return 0;
}
答案 0 :(得分:3)
简短回答:不,在规则初始化期间,语义操作不评估。
但是,您的问题是您没有(只是)编写语义操作。
是的,在您的情况下,该表达式的第一部分
std::cout << "string=" << _1 << std::endl
有副作用:(std::cout << "string=")
将文字插入标准ostream对象,然后通过引用返回std::cout
。
这是因为您使用标准库中定义的std::operator<<
,而不是boost::phoenix::....::operator<<
¹。
您可以通过强制将第二个参数的类型修改为选择正确重载的内容:
std::cout << phx::val("string=") << _1 << std::endl
当然,您可以通过以下方式做出双重保证:
phx::ref(std::cout) << _1 << std::endl
但是你会注意到,只要有可能,人们就会跳过这个。第二个操作数boost::spirit::_1
已经引入表达式模板上下文(即选择构造惰性actor的非标准运算符重载而不是副作用)。
¹无论如何可能最终只是boost::proto::....::operator<<
,但这都是美味的实施细节:)