【问题标题】:Constructing a qi::rule with a function attribute构造具有函数属性的 qi::rule
【发布时间】:2014-09-23 05:51:34
【问题描述】:

我正在尝试创建一个返回 function<char(char const *)> 的规则,该 function<char(char const *)> 是通过对 Phoenix 表达式进行柯里化而构建的。例如,

start = int_[_val = xxx];
rule<Iterator, function<char(char const *)> start;

xxx 应该是什么,以便解析字符串 "5" 应该给我一个函数,给我输入的第五个字符?我已经尝试过lambda(_a = arg1)[arg1[_a]](_1) 之类的方法可能会起作用,但我无法找到神奇的公式。

换句话说,我希望属性在解析后的 int 值上 curry arg2[arg1]

非常感谢任何建议。请注意,我使用的是 VC2008,因此 C++11 lambdas 不可用。

迈克

【问题讨论】:

    标签: c++ boost-spirit boost-spirit-qi higher-order-functions expression-templates


    【解决方案1】:

    修复该规则声明后:

    typedef boost::function<char(char const*)> Func;
    qi::rule<Iterator, Func()> start;
    

    成功了:Live On Coliru (c++03)。

    更新

    为什么我最终得到了如此复杂的装置?

    qi::_val = px::bind(px::lambda[arg1[arg2]], px::lambda[arg1], qi::_1)
    

    嗯。让我告诉您将函数组合与惰性求值复杂化的乐趣(在 C++ 模板元编程中,具有引用/值语义的这些惊喜):不要执行以下操作:

    qi::_val = px::lambda(_a = qi::_1) [arg1[_a]] // UB!!! DON'T DO THIS
    

    根据编译器和优化级别,这可能*出现可以工作。但它调用了未定义的行为[1]。问题是qi::_1 将作为qi::int_ 解析器表达式公开的属性的引用。但是,在解析器上下文的生命周期结束后,此引用是悬空引用。

    因此,通过无效引用评估函子间接。为避免这种情况,您应该说 (Live On Coliru):

    qi::_val = px::lambda(_a = px::val(qi::_1)) [arg1[_a]]
    

    甚至(如果你喜欢晦涩的代码):

    qi::_val = px::lambda(_a = +qi::_1) [arg1[_a]]
    

    或者,您知道,您可以坚持使用绑定的嵌套 lambda,因为将 defaults 绑定到 qi::_1 的值语义(除非您使用 phx::cref/phx::ref 包装器) .

    我希望上面的分析能够让我明白我之前在 cmets 中提出的观点:

    请注意,我不推荐这种代码风格。使用 Phoenix 进行高阶编程已经足够棘手,无需在某些嵌入式表达式模板 DSL 中从惰性 actor 中组合它们:qi::_val = px::bind(px::lambda[arg1[arg2]], px::lambda[arg1], qi::_1)'Nuff 说


    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/spirit/include/phoenix_operator.hpp>
    #include <boost/function.hpp>
    
    namespace qi = boost::spirit::qi;
    namespace px = boost::phoenix;
    
    typedef boost::function<char(char const*)> Func;
    
    int main()
    {
        typedef std::string::const_iterator Iterator;
        using namespace boost::phoenix::arg_names;
    
        qi::rule<Iterator, Func()> start;
    
        start = qi::int_ 
                [ qi::_val = px::bind(px::lambda[arg1[arg2]], px::lambda[arg1], qi::_1) ];
        // or:  [ qi::_val = px::lambda(_a = px::val(qi::_1))[arg1[_a]] ];
    
        static char const* inputs[] = { "0", "1", "2", "3", "4", 0 };
    
        for (char const* const* it = inputs; *it; ++it)
        {
            std::string const input(*it);
            Iterator f(input.begin()), l(input.end());
    
            Func function;
            bool ok = qi::parse(f, l, start, function);
    
            if (ok)
                std::cout << "Parse resulted in function() -> character " 
                   << function("Hello") << "; " 
                   << function("World") << "\n";
            else
                std::cout << "Parse failed\n";
    
            if (f != l)
                std::cout << "Remaining unparsed: '" << std::string(f, l) << "'\n";
        }
    }
    

    打印

    Parse resulted in function() -> character H; W
    Parse resulted in function() -> character e; o
    Parse resulted in function() -> character l; r
    Parse resulted in function() -> character l; l
    Parse resulted in function() -> character o; d
    

    [1](MSVC2013 出现崩溃,gcc 可能在 -O3 中工作,但在 -O0 等中出现段错误)

    【讨论】:

    • 感谢您的回复,sehe,但不幸的是,我认为这不能解决我的问题。除非我遗漏了什么,否则上面的代码似乎忽略了已解析的整数,这是我试图合并的。我想要的是如果输入是"2",那么output("Hello") 应该是e。你知道我该怎么做吗?
    • @user2913094 好了。我更新了示例以使其更加完整。请注意,我不推荐这种代码风格。使用 Phoenix 进行高阶编程已经足够棘手,无需在某些嵌入式表达式模板 DSL 中从惰性 actor 中编写它们:qi::_val = px::bind(px::lambda[arg1[arg2]], px::lambda[arg1], qi::_1)'纳夫说`?
    • 似乎是个好建议。替代方案(例如,创建一个仿函数类并完全避免使用 lambda)有点笨拙,但至少它们是可以理解的。谢谢!
    • @user2913094 我添加了一个更优雅但非常微妙棘手的更新版本。我还解释了为什么它如此棘手。结论:我不推荐将此用于生产代码
    • 我不得不修改你更优雅的解决方案
    猜你喜欢
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-16
    • 2023-04-02
    • 1970-01-01
    相关资源
    最近更新 更多