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

Semantic action evaluated in grammar constructor (or not?)

本文关键字:评估 操作 语法 构造函数 语义      更新时间:2023-10-16

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

简短回答:否,在规则初始化期间评估语义操作。

但是,您的问题是您没有(只是)编写语义操作。


是的,在您的情况下,该表达式的第一部分

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的第二个操作数已经诱导了表达式模板上下文(即选择构造惰性参与者而不是副作用的非标准运算符重载)。


¹ 无论如何,最终可能只是boost::proto::....::operator<<,但这些都是美味的实现细节:)