【问题标题】:Boost spirit changing variable value in semantic action在语义动作中提升精神变化变量值
【发布时间】:2014-05-21 16:09:07
【问题描述】:

我想改变语义动作中的局部变量值,如下所示:

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>

namespace qi = boost::spirit::qi;
namespace spirit = boost::spirit;
namespace ascii = boost::spirit::ascii;
using boost::phoenix::ref;
using boost::phoenix::bind;

void dummy(const std::vector<char>& v, int& var)
{
    var = 7;
}

template <typename Iterator>
struct x_grammar : public qi::grammar<Iterator, std::string(), ascii::space_type>
{
public:
    x_grammar() : x_grammar::base_type(start_rule, "x_grammar")
    {
        using namespace qi;
        int local_var = 0;
        start_rule = (+(char_ - ";"))[bind(dummy, _1, ref(local_var))];
        //repeat(ref(local_var))[some_rule];
    }
private:
    qi::rule<Iterator, std::string(), ascii::space_type> start_rule;
};

int main()
{
    typedef std::string::const_iterator iter;
    std::string storage("string;aaa");
    iter it_begin(storage.begin());
    iter it_end(storage.end());
    std::string read_data;
    using boost::spirit::ascii::space;
    x_grammar<iter> g;
    try {
        bool r = qi::phrase_parse(it_begin, it_end, g, space, read_data);
        std::cout << "Pass!\n";
    } catch (const qi::expectation_failure<iter>& x) {
        std::cout << "Error!\n";
    }
}

我在使用 GCC 4.6.1 和 boost 1.55 时遇到一些烦人的编译错误。

【问题讨论】:

  • 局部变量只“存在”到构造函数结束。如果您想做类似的事情,您需要使变量成为语法的成员(或者可能将其作为参数传递给构造函数)。同样使用 boost::phoenix::bind(或 ref)和 c++11 编译器在没有命名空间限定的情况下与命名空间 std(在这种情况下为 std::vector)中的类的参数一起使用时会出现 ADL 问题。你应该使用类似namespace phx=boost::phoenix;... phx::bind(dummy,_1,phx::ref(local_var));的东西。

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


【解决方案1】:

我不禁要注意,如果编译器错误让您烦恼,那么也许您应该编写有效代码:/


指导性帽子...

这当然是一句轻率的话,但也有点启发性。

我已经告诉过你两次,现在在你的语法中使用构造函数局部变量的整个想法从根本上被打破了

你想要的是

  • 继承的属性
  • qi::locals
  • maayyyyybe,maaaayyybe语法成员变量;需要注意的是,它们使您的规则不可重入。

真正深入你脑海的重要事情是

Boost Spirit 从表达式模板生成解析器。表达式模板是 90% 的静态信息(仅限类型),并被“编译”(.compile())成“可调用”(.parse())形式。

最重要的是,虽然您可以在语义操作中编写控制流,但这些都没有在定义站点实际执行。它被“编译”成一个 lazy actor,以后可以调用它。

当对应的解析表达式匹配时,生成的解析将有条件地调用惰性actor


建设性的帽子...

看起来您只想使用函数转换属性。

您可以这样做:

  1. transform 作为语义动作的一部分,将结果放入常规属性中(为解析器组合维护“功能”语义):

    qi::rule<Iterator, exposed(), Skipper> myrule;
    myrule = int_ [ _val = phx::bind(custom_xform, _1) ];
    

    custom_xform 是任何老式可调用对象(包括多态对象):

    exposed custom_xform(int i) { return make_new_exposed(i); } 
    // or
    struct custom_xfrom_t {
    
      template <typename> struct result { typedef exposed type; };
    
      template <typename Int>
        exposed operator()(Int i) const {
            return make_new_exposed(i);
        }
    };
    static const custom_xform_t custom_xform;
    
  2. 你可以添加一些语法糖[1]

    qi::rule<Iterator, exposed(), Skipper> myrule;
    myrule = int_ [ _val = custom_xform(_1) ];
    

    这要求custom_xform被定义为一个惰性actor:

    phx::function<custom_xform_t> custom_xform; // `custom_xform_t` again the (polymorphic) functor
    

    您可能会注意到这不适用于常规功能。你可以将它包装在一个可调用对象中,或者使用BOOST_PHOENIX_ADAPT_FUNCTION 宏来为你做这件事

  3. 如果您想要更频繁地应用一些更复杂的转换,请考虑使用 Spirit 自定义点:

    如果您为属性选择特定类型(例如Ast::MultiplicityAst::VelocityRanking,而不是intdouble,则这些工作最顺利


[1] 使用 BOOST_SPIRIT_USE_PHOENIX_V3

【讨论】:

  • 将在一分钟内添加一些(甚至更多)建设性的 cmets。
  • 好的,也添加了更多建设性的内容。我相信你可以用这个修复hidden XY Problem。祝你好运。
  • 感谢您的所有帮助。我想你是对的,我不了解升压精神的一些基本原理。我只是没有足够的时间去做。
  • 致反对者:如果您不能轻易地观察到答案是由建构主义和大量的主题信息主导,那么它说明了很多关于您的信息,然而您发现在 2 年后否决答案很重要。
【解决方案2】:

使用 C++03 的代码 compiles。但是,当使用 GCC 4.6 的 C++11 支持时,代码fails to compile。以下是错误的相关摘录:

/usr/local/include/boost/spirit/home/support/action_dispatch.hpp:在静态 成员函数'static void boost::spirit::traits::action_dispatch::caller(F&&, A&& ...) [with F = const std::_Bind]' ... main.cpp:25:9: 从 'x_grammar::x_grammar() [...] 实例化 /usr/local/include/boost/spirit/home/support/action_dispatch.hpp:142:13:错误: 没有匹配函数调用'boost::spirit::traits:: action_dispatch<...>::do_call(const std::_Bind)'

尽管有using boost::phoenix::bind 指令,对bind() 的非限定调用解析为std::bind() 而不是boost::phoenix::bind(),但参数解析为Boost.Phoenix 演员。 Boost.Spirit documentation 特别警告不要混合来自不同库的占位符:

您必须确保不要将占位符与它们不属于的库混合使用,并且在编写语义操作时不要使用不同的库。

因此,编译问题可以通过明确定义语义动作来解决。使用任一:

std::bind(dummy, std::placeholders::_1, std::ref(local_var))

或:

boost::phoenix::bind(dummy, _1, ref(local_var))

虽然这解决了编译器错误,但值得注意的是 ref(local_var) 对象将保持一个悬空引用,因为它的生命周期超出了 local_var 的生命周期。这是一个有效的example,其中local_var 的生命周期通过使其成为静态而扩展到构造函数的范围之外。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-27
    • 1970-01-01
    相关资源
    最近更新 更多