7

我的目标是创建一个解决方法,以便我可以在 Boost Spirit Qi 语义操作中使用 C++11 lambda,同时仍然可以访问更扩展的 qi 占位符集,例如 qi::_pass 或 qi::_r1,无需必须手动从上下文对象中提取它们。我希望避免为一些重要的解析逻辑编写 Phoenix lambda,而是更喜欢 C++11 lambda 中可用的更直接的 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 构造会更简单,或者甚至可以通过允许直接使用单参数 C++11 lambda 的 Boost Spirit 更新来完成,因为它只使用解析后的值 (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 &
)'
4

1 回答 1

4

告诉 Boost 你想要最前沿的编译器支持:[1]

#define BOOST_RESULT_OF_USE_DECLTYPE

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

#define BOOST_SPIRIT_USE_PHOENIX_V3

它有效

在 Coliru 上看到它

原因:

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

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

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

  • 在第二个#define方面,您需要选择 Phoenix V3,因为 Phoenix V2 根本没有实现对 lambdas 的支持。默认情况下,Spirit V2 出于历史/兼容性原因选择 Phoenix V2。在实践中,Phoenix V3 更加成熟并且修复了很多(很多很多)问题,所以我建议总是运行BOOST_SPIRIT_USE_PHOENIX_V3


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

于 2013-11-12T07:53:35.300 回答