C++ 无法从Boost.Spirit中的占位符构造std::字符串

C++ 无法从Boost.Spirit中的占位符构造std::字符串,c++,boost-spirit,boost-spirit-qi,C++,Boost Spirit,Boost Spirit Qi,我已经开始研究一个基于Boost.Spirit的简单解析器,它将解析一个类似C++的文件(唯一的C++-ish部分是内置的模板类型;例如,map name\u object\u map;——但这是内置的编译器,用户不能声明模板类)。然而,语法旨在包含数据结构声明,而不是表达式,用于初始化枚举器声明的常量表达式除外枚举E{a=4*5+3}是有效的。这目前对我来说不是问题,因为我还不能以我想要的方式解析E) 在阅读了文档和示例之后,我昨天做了以下内容,但没有编译: #include <boos

我已经开始研究一个基于Boost.Spirit的简单解析器,它将解析一个类似C++的文件(唯一的C++-ish部分是内置的模板类型;例如,
map name\u object\u map;
——但这是内置的编译器,用户不能声明模板类)。然而,语法旨在包含数据结构声明,而不是表达式,用于初始化枚举器声明的常量表达式除外<代码>枚举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);
}
foo_decl
s语义动作中,它不能构造
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
it,我可以看到
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
不起作用

谢谢大家!

首先:

  • 我会在精神上引导克莱尔远离聪明的指针

  • 如果你不需要语义动作,我会避开它们(你不需要)

  • 占位符的问题在于:您不能静态地使用它,您需要在惰性表达式中使用它(Phoenix Actor)

从第一个链接使用phoenix::函数:

#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";

}
以及的调试输出

<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>

富约翰多{};
富约翰多{};
约翰多{};
{ };
[j,o,h,n,d,o,e]]
[0x60600000ebb0]
[[0x60600000ebb0]]
首先:

  • 我会在精神上引导克莱尔远离聪明的指针

  • 如果你不需要语义动作,我会避开它们(你不需要)

  • 占位符的问题在于:您不能静态地使用它,您需要在惰性表达式中使用它(Phoenix Actor)

从第一个链接使用phoenix::函数:

#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";

}
以及的调试输出

<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>

富约翰多{};
富约翰多{};
约翰多{};
{ };
[j,o,h,n,d,o,e]]
[0x60600000ebb0]
[[0x60600000ebb0]]

稍后我会回来查看您的具体示例。谢谢!我想我可以不使用
共享\u ptr
,但我不明白如果没有语义动作我怎么办。解析标识符后,如何使用匹配的标识符构造一个
foo_decl
?我已经用您的特定示例更新了演示:。对于“父上下文”指针,您似乎只想构建一个递归AST树。我至少有几十个这样的例子,但鉴于你的语法,我看不出你需要它。谢谢你的现场演示。我将搜索您的示例,尤其是不使用语义操作的解决方案<代码>父上下文对应于名称的声明性区域。因此,在
struct S{enum E{a};ee;} Stutts是<代码> E< /COD>的父上下文,很像C++。但是,您的示例不解析任何类似的内容:)使AST简单递归(查看我的答案示例),并将运行时上下文与解析分开,$0.05我稍后会回来查看您的具体示例。谢谢!我想我可以不使用
共享\u ptr
,但我不明白如果没有语义动作我怎么办。解析标识符后,如何使用匹配的标识符构造一个
foo_decl
?我已经用您的特定示例更新了演示:。对于“父上下文”指针,您似乎只想构建一个递归AST树。我至少有几十个这样的例子,但鉴于你的语法,我看不出你需要它。谢谢你的现场演示。我将搜索您的示例,尤其是不使用语义操作的解决方案<代码>父上下文
对应于名称的声明性区域。因此,在
struct S{enum E{a};ee;} Stutts是<代码> E< /COD>的父上下文,很像C++。但是,你的示例不解析任何类似的内容:)让AST简单递归(查看我的答案示例),并将运行时上下文与解析0.05美元分开。