【问题标题】:Phoenix::bind for C++11 lambdas in boost::spirit::qi semantic actionsPhoenix::bind for C++11 lambdas in boost::spirit::qi 语义动作
【发布时间】:2013-11-12 07:15:55
【问题描述】:

我的目标是创建一个解决方法,以便我可以在 Boost Spirit Qi 语义操作中使用 C++11 lambda,同时仍然可以访问更扩展的 qi 占位符集,例如 qi::_pass 或 qi:: _r1,无需手动从上下文对象中提取它们。我希望避免为一些重要的解析逻辑编写 Phoenix lambdas,而是更喜欢 C++11 lambdas 中可用的更直接的 C++ 语法和语义。

下面的代码代表了我对解决方法的想法。这个想法是使用 phoenix::bind 绑定到 lambda 并将我需要的特定占位符传递给它。但是,我收到了一个非常长的模板化编译器错误(gcc 4.7.0,Boost 1.54),我没有专业知识来解释。我选择了我认为最相关的部分并将其发布在代码下方。

我想知道我在这段代码中尝试做的事情是否可以通过 Boost Spirit 实现,以及是否有人可以为我解释错误消息并告诉我出了什么问题。

#include <string>
#include <iostream>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
namespace spirit = boost::spirit;
namespace phoenix = boost::phoenix;

int main() {

    std::string input{"test1 test2 test3 FOO!"};
    typedef decltype(input.begin()) StringIter;

    qi::rule<StringIter, std::string()> parser =
        *(
            qi::char_
            [
                phoenix::bind(
                    [] (char value) {
                        std::cerr << value << std::endl;
                    },
                    qi::_1
                )
            ]
        );

    qi::parse(input.begin(), input.end(), parser);
}

(注意:我知道此代码执行的特定任务使用直接 Phoenix 构造会更简单,或者甚至可以通过 Boost Spirit 更新来完成,该更新允许直接使用单参数 C++11 lambda,因为它只使用解析后的值 (qi::_1)。不过,这是我想做的那种事情的一个很好的最小示例,如果我可以让它工作,它应该很容易泛化。)

还有一点编译器错误(通过 STLfilt):

test\testSpiritLearning.cpp:28:9:   required from here
D:\programming\lib\boost\boost_1_54_0/boost/spirit/home/support/action_dispatch.hpp:178:13:
error: no match for call to '(
    const boost::phoenix::actor<
        boost::phoenix::composite<
            boost::phoenix::detail::function_eval<1>
          , boost::fusion::vector<
                boost::phoenix::value<main()::<lambda(char &)> >
              , boost::spirit::argument<0>, boost::fusion::void_
              , boost::fusion::void_, boost::fusion::void_
              , boost::fusion::void_, boost::fusion::void_
              , boost::fusion::void_, boost::fusion::void_
              , boost::fusion::void_
            >
        >
    >
) (
    boost::spirit::traits::pass_attribute<
        boost::spirit::qi::char_class<
            boost::spirit::tag::char_code<
                boost::spirit::tag::char_
              , boost::spirit::char_encoding::standard
            >
        >, char, void
    >::type &
  , boost::spirit::context<
        boost::fusion::cons<basic_string<char> &, boost::fusion::nil>
      , boost::fusion::vector0<>
    > &, bool &
)'

【问题讨论】:

    标签: c++ c++11 lambda boost-spirit


    【解决方案1】:

    只需告诉 Boost 您想要最先进的编译器支持即可:[1]

    #define BOOST_RESULT_OF_USE_DECLTYPE
    

    并且您希望使用 Phoenix V3 版本:

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    

    它有效

    Live on Coliru

    原因:

    • 在 Phoenix actors 中使用函数对象假定您的函数对象将有一个特殊的嵌套 struct result 模板,或者实际上是一个简单的 typedef result_type。这称为 RESULT_OF 协议,请参见此处:

      http://www.boost.org/doc/libs/1_55_0/libs/utility/utility.htm#result_of

      c++03 兼容性需要此协议。但是,lambdas 没有。事实上,lambda 具有未指定的类型。这正是支持 lambda 的编译器也总是有 decltype 的原因之一,因此不再需要 RESULT_OF 协议

    • 在第二个#define,您需要选择Phoenix V3,因为Phoenix V2 根本不支持lambdas。默认情况下,Spirit V2 出于历史/兼容性原因选择 Phoenix V2。在实践中,Phoenix V3 更加成熟并修复了很多(很多)问题,所以我建议始终使用BOOST_SPIRIT_USE_PHOENIX_V3


    [1] 对于某些编译器的最新版本可能不需要

    【讨论】:

    • 非常感谢,这确实修复了编译器错误。出于好奇,我还尝试了将每个添加依次注释掉;但它只适用于未注释的两者。现在我很想知道为什么会奏效。
    • @anthrond 当然,我提到 both 是有原因的 :) 我已经添加了一些解释。干杯
    • 所以我刚刚注意到,如果我 将 lambda 包装在 phoenix::bind() 中,它也可以正确编译和运行,但只需直接传递它到语义操作(类似于我见过的其他一些问题,Spirit 现在处理一些具有特定签名的 lambda),并删除您添加的两个 #defines。因此,根据您的评论,只有 Phoenix V3 可以处理 lambda,这使我得出结论,Spirit 现在可以直接处理 lambda,但不能直接处理 Phoenix V2,因此在 Spirit 和 lambda 之间引入 Phoenix V2 层是导致我的错误。
    • @anthrond 是的。只是,您没有介绍该层:默认情况下,Spirit 与 Phoenix V2 结婚。即使没有phx::bind,您也只能隐式获得 Phoenix V2。此外,请检查语义是否实际上相同。如果它编译,这并不意味着它会做你所期望的(惰性/非惰性函数......)。但是,由于隐式转换为函数指针类型, 可能仅传递无状态 lambda(未调用)即可。 see here e.g.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多