Phoenix :: bind for C++ 11 lambdas在boost :: spirit :: qi语义动作中

Ant*_*all 6 c++ lambda boost-spirit c++11

我的目标是创建一个解决方法,以便我可以在Boost Spirit Qi语义操作中使用C++ 11 lambdas,同时仍然可以访问更多扩展的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);
}
Run Code Online (Sandbox Code Playgroud)

(注意:我知道这个代码执行的特定任务对于直接的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 &
)'
Run Code Online (Sandbox Code Playgroud)

seh*_*ehe 4

只需告诉 Boost 您需要前沿编译器支持:[1]

#define BOOST_RESULT_OF_USE_DECLTYPE
Run Code Online (Sandbox Code Playgroud)

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

#define BOOST_SPIRIT_USE_PHOENIX_V3
Run Code Online (Sandbox Code Playgroud)

它有效

观看Coliru 直播

原因:

  • 在 Phoenix Actor中使用函数对象假设您的函数对象将有一个特殊的嵌套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 根本不实现对 lambda 的支持。默认情况下,出于历史/兼容性原因,Spirit V2 选择 Phoenix V2。在实践中,Phoenix V3 更加成熟并且修复了许多(很多)问题,所以我建议始终运行BOOST_SPIRIT_USE_PHOENIX_V3


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

  • @anthrond 是的。只是,你没有_引入_那个层:Spirit默认与Phoenix V2_结婚_。即使没有 `phx::bind`,您也只能隐式获得 Phoenix V2。另外,检查语义是否实际上相同。如果它可以编译,这并不意味着它会执行您所期望的操作(惰性/非惰性函数...)。然而,由于隐式转换为函数指针类型,仅传递无状态 lambda(未调用)可能会起作用。[参见此处例如](http://stackoverflow.com/questions/7852101/c-lambda-with-captures-as-a-function-pointer) (2认同)