【问题标题】:cannot construct std::string from placeholder in Boost.Spirit无法从 Boost.Spirit 中的占位符构造 std::string
【发布时间】:2016-07-06 10:22:20
【问题描述】:

我已经开始研究基于 Boost.Spirit 的简单解析器,它将解析类 C++ 文件(唯一的 C++ 部分是内置模板类型;例如 map<string, smart_ptr<int>> name_object_map; - 但这是内置的编译器,用户不能声明模板类)。然而,语法旨在包含数据结构声明,而不是表达式,除了用于初始化枚举器声明的常量表达式; enum E { a = 4 * 5 + 3 }; 有效。这对我来说目前不是问题,因为我还不能以我想要的方式解析 E :)

在阅读文档和示例后,我昨天做了以下操作,但它没有编译:

#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <cassert>
#include <memory>
#include <string>
#include <utility>

struct context {};

class foo {
  std::string name;
  const context *ctx;

public:
  foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {}
};

using foo_ref = std::shared_ptr<foo>;

template <typename Iterator>
struct skipper : boost::spirit::qi::grammar<Iterator> {
  skipper() : skipper::base_type(start) {
    using namespace boost::spirit;
    qi::char_type char_;
    ascii::space_type space;

    start = space                             // tab/space/cr/lf
            | "/*" >> *(char_ - "*/") >> "*/" // C-style comments
        ;
  }

  boost::spirit::qi::rule<Iterator> start;
};

template <typename Iterator>
struct the_parser : boost::spirit::qi::grammar<Iterator, std::vector<foo_ref>(),
                                               skipper<Iterator>> {
  the_parser() : the_parser::base_type(start), current_context(&root) {
    using namespace boost::spirit;
    namespace phx = boost::phoenix;

    identifier = qi::lexeme[qi::alpha >> *qi::alnum];
    start = *(foo_decl); // currently, no semantic action attached.
                         // This will create the root decl in ast.

    foo_decl = (lit("foo") >> identifier)[qi::_val = std::make_shared<foo>(
                                               qi::_1, current_context)] >>
               qi::char_('{') >> qi::char_('}') >> qi::char_(';');
    BOOST_SPIRIT_DEBUG_NODES((identifier)(start)(foo_decl));
  }
  boost::spirit::qi::rule<Iterator, std::string(), skipper<Iterator>>
      identifier;
  boost::spirit::qi::rule<Iterator, std::vector<foo_ref>(), skipper<Iterator>>
      start;
  boost::spirit::qi::rule<Iterator, foo_ref(), skipper<Iterator>> foo_decl;
  context root;
  const context *current_context;
};

int main() {
  the_parser<std::string::const_iterator> parser;
  std::vector<foo_ref> root;

  const std::string content = "foo john_doe { };";
  auto first = content.cbegin(), last = content.cend();
  bool r = boost::spirit::qi::phrase_parse(
      first, last, parser, skipper<std::string::const_iterator>(), root);
  assert(r && first == last);
}

在 Mac 上用 clang 编译这个(第一行是std::make_shared):

error: no matching constructor for initialization of 'foo'
          __second_(_VSTD::forward<_Args2>(_VSTD::get<_I2>(__second_args))...)
          ^         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
note: candidate constructor not viable: no known conversion from 'const boost::phoenix::actor<boost::spirit::argument<0> >' to 'const std::string' (aka
  'const basic_string<char, char_traits<char>, allocator<char> >') for 1st argument
  foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {}
  ^

foo_decls语义动作中,不能构造foo(通过std::make_shared),因为第一个属性的结果不能转换为std::string。但是,如果我添加一个类成员 std::string s,并改为这样做,它会起作用:

foo_decl = (lit("foo") >> identifier)[boost::phoenix::ref(s) = qi::_1] >>
                 qi::char_('{') >> qi::char_('}') >> qi::char_(';');

同样,如果我尝试 std::cout 它,我可以看到 john_doe 被打印出来。

如果我将成员函数调用与 phoenix 绑定,它也可以:

foo_decl = (lit("foo") >> identifier)[qi::_val =
                   boost::phoenix::bind(&the_parser, this, qi::_1)] >>
           qi::char_('{') >> qi::char_('}') >> qi::char_(';');

foo_ref make_foo(const std::string &n) {
  return std::make_shared(n, current_context);
}

这最后三个变通方法意味着存在从decltype(qi::_1)std::string 的隐式转换序列;这不正确吗?

如果您能告诉我我的错误或我对语义操作和占位符如何工作的理解上的差距,我会非常高兴。为什么std::make_shared 不起作用,我觉得很奇怪。

谢谢!

【问题讨论】:

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


    【解决方案1】:

    首先:

    使用第一个链接中的 phoenix::function:

    Live On Coliru

    #define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <cassert>
    #include <memory>
    #include <string>
    #include <utility>
    
    namespace {
        template <typename T> struct make_shared_f {
            template <typename... A> struct result { typedef std::shared_ptr<T> type; };
    
            template <typename... A> typename result<A...>::type operator()(A &&... a) const {
                return std::make_shared<T>(std::forward<A>(a)...);
            }
        };
    
        template <typename T> using make_shared_ = boost::phoenix::function<make_shared_f<T> >;
    }
    
    struct context {};
    
    class foo {
        std::string name;
        const context *ctx;
    
      public:
        foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {}
    };
    
    using foo_ref = std::shared_ptr<foo>;
    
    template <typename Iterator> struct skipper : boost::spirit::qi::grammar<Iterator> {
        skipper() : skipper::base_type(start) {
            using namespace boost::spirit;
            qi::char_type char_;
            ascii::space_type space;
    
            start = space                             // tab/space/cr/lf
                    | "/*" >> *(char_ - "*/") >> "*/" // C-style comments
                ;
        }
    
        boost::spirit::qi::rule<Iterator> start;
    };
    
    template <typename Iterator>
    struct the_parser : boost::spirit::qi::grammar<Iterator, std::vector<foo_ref>(), skipper<Iterator> > {
        the_parser() : the_parser::base_type(start), current_context(&root) {
            using namespace boost::spirit;
            namespace phx = boost::phoenix;
    
            identifier = qi::alpha >> *qi::alnum;
            // This will create the root decl in ast.
    
            foo_decl = ("foo" >> identifier) [qi::_val = make_shared_<foo>{}(qi::_1, current_context)] >>
                       '{' >> '}' >> ';';
    
            start      = *foo_decl; // currently, no semantic action attached.
            BOOST_SPIRIT_DEBUG_NODES((identifier)(start)(foo_decl));
        }
        boost::spirit::qi::rule<Iterator, std::string()> identifier;
        boost::spirit::qi::rule<Iterator, foo_ref(), skipper<Iterator> > foo_decl;
        boost::spirit::qi::rule<Iterator, std::vector<foo_ref>(), skipper<Iterator> > start;
        context root;
        const context *current_context;
    };
    
    int main() {
        the_parser<std::string::const_iterator> parser;
        std::vector<foo_ref> root;
    
        const std::string content = "foo johndoe { };";
        auto first = content.cbegin(), last = content.cend();
        bool r = boost::spirit::qi::phrase_parse(first, last, parser, skipper<std::string::const_iterator>(), root);
        if (r)
            std::cout << "success\n";
        else
            std::cout << "failed\n";
    
        if (first != last)
            std::cout << "remaining unparsed: '" << std::string(first,last) << "'\n";
    
    }
    

    打印

    success
    

    连同

    的调试输出
    <start>
      <try>foo johndoe { };</try>
      <foo_decl>
        <try>foo johndoe { };</try>
        <identifier>
          <try>johndoe { };</try>
          <success> { };</success>
          <attributes>[[j, o, h, n, d, o, e]]</attributes>
        </identifier>
        <success></success>
        <attributes>[0x60600000ebb0]</attributes>
      </foo_decl>
      <foo_decl>
        <try></try>
        <fail/>
      </foo_decl>
      <success></success>
      <attributes>[[0x60600000ebb0]]</attributes>
    </start>
    

    【讨论】:

    • 我稍后会回来看看你的具体例子。
    • 谢谢!我想我可以没有shared_ptr,但我不明白我怎么能没有语义动作。解析标识符后,如何构造一个匹配标识符的foo_decl
    • 我已经根据您的具体示例更新了演示:Live on Coliru。 W.r.t. “父上下文”指针,看起来你只是想构建一个递归 AST 树。我在Stack Overflow 上至少有几十个这样的例子,但考虑到你的语法,我看不出你需要什么。
    • 感谢现场演示。我将在 SO 中搜索您的示例,特别是 不使用 语义操作的解决方案。 parent_context 对应于名称的声明区域。所以,在struct S { enum E { a }; E e; }; 中,struct SE 的父上下文,很像C++。
    • 没关系,但是您的示例不会解析类似的内容 :) 使您的 AST 简单地递归(查看我的答案以获取示例)并将您的 runtime 上下文与解析分开, $0.05
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-27
    • 2016-05-02
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 2015-04-26
    相关资源
    最近更新 更多