在语法构造函数中评估的语义操作(或不?)

时间:2014-11-21 21:20:19

标签: c++ boost-spirit boost-phoenix

我一直在学习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;
}

1 个答案:

答案 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<<,但这都是美味的实施细节:)