【问题标题】:String parser with boost variant recursive wrapper具有 boost 变体递归包装器的字符串解析器
【发布时间】:2012-07-10 20:28:46
【问题描述】:

以下代码(改编自 Spirit qi mini_xml 示例)无法编译。存在与具有递归 boost::variant 属性的规则 brac 相关的错误。
但是,所有注释掉的brac 版本都可以编译。

我很想知道是什么让简单的字符串解析器在这种情况下如此特别:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>

#include <string>
#include <vector>

namespace client
{
   namespace fusion = boost::fusion;
   namespace phoenix = boost::phoenix;
   namespace qi = boost::spirit::qi;
   namespace ascii = boost::spirit::ascii;

   struct ast_node;

   typedef boost::variant<
      boost::recursive_wrapper<ast_node>,
      std::string
   > ast_branch;

   struct ast_node
   {
      std::string text;
      std::vector<ast_branch> children;
   };
}

BOOST_FUSION_ADAPT_STRUCT(
      client::ast_node,
      (std::string, text)
      (std::vector<client::ast_branch>, children)
)

namespace client
{
   template <typename Iterator>
      struct ast_node_grammar
      : qi::grammar<Iterator, ast_branch(), ascii::space_type>
      {
         ast_node_grammar()
            : ast_node_grammar::base_type(brac)
         {
            using qi::_1;
            using qi::_val;
            using ascii::char_;
            using ascii::string;

            name %= *char_;

            brac %= string("no way") ;
//            brac = string("works")[_val = _1] ;
//            brac %= string("this") | string("works");
//            brac %= name ; // works
//            brac %= *char_ ; // works
         }
         qi::rule<Iterator, std::string()> name;
         qi::rule<Iterator, ast_branch(), ascii::space_type> brac;
      };
}


int main(int argc, char **argv)
{
   typedef client::ast_node_grammar<std::string::const_iterator> ast_node_grammar;
   ast_node_grammar gram;
   client::ast_branch ast;

   std::string text("dummy");
   using boost::spirit::ascii::space;
   std::string::const_iterator iter = text.begin();
   std::string::const_iterator end = text.end();
   bool r = phrase_parse(iter, end, gram, space, ast);
   return r ? 0 : 1;
}

部分错误信息:

/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:38:17: error: No match for ‘boost::variant<
        boost::recursive_wrapper<client::ast_node>, basic_string<char> 
>::variant(
        const __normal_iterator<const char *, basic_string<char> > &, const __normal_iterator<
            const char *, basic_string<char> > &)’

提前致谢。

【问题讨论】:

    标签: c++ parsing boost-spirit boost-variant


    【解决方案1】:

    我建议问题是属性兼容性。与the documentation 相反,ascii::string 解析器似乎公开了一个 iterator range 而不是 string

    name = string("no way");
    

    没问题,因为 ascii::string 暴露的属性可以毫无困难地强制转换为规则的属性类型。

    但是,brac 规则的属性类型是ast_branch,它只是一个变体,其中一种可能包含的类型。因此,ast_branch 类型有多个构造函数,Spirit 并不清楚哪个构造函数适合这种特定的转换。

    有几种方法(除了您已经展示的方法):

    • 使用attr_cast

      brac = qi::attr_cast( string("no way") );
      
    • 使用as_string

      brac = qi::as_string[ string("no way") ];
      
    • 使用自定义点

      namespace boost { namespace spirit { namespace traits {
          template <typename It>
              struct assign_to_attribute_from_iterators<client::ast_branch, It>
              {
                  static void call(It const& f, It const& l, client::ast_branch& val)
                  {
                      val = std::string(f, l);
                  }
              };
      }}}
      

    每一个都有相同的效果:让Spirit实现使用什么属性转换。

    这是一个完整的工作示例,显示了所有三个:

    // #define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
    #include <boost/config/warning_disable.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/variant/recursive_variant.hpp>
    
    #include <string>
    #include <vector>
    
    namespace client
    {
       namespace fusion  = boost::fusion;
       namespace phoenix = boost::phoenix;
       namespace qi      = boost::spirit::qi;
       namespace ascii   = boost::spirit::ascii;
    
       struct ast_node;
    
       typedef boost::variant<
          boost::recursive_wrapper<ast_node>,
          std::string
       > ast_branch;
    
       struct ast_node
       {
          std::string text;
          std::vector<ast_branch> children;
       };
    }
    
    namespace boost { namespace spirit { namespace traits {
        template <typename It>
            struct assign_to_attribute_from_iterators<client::ast_branch, It>
            {
                static void call(It const& f, It const& l, client::ast_branch& val)
                {
                    val = std::string(f, l);
                }
            };
    }}}
    
    BOOST_FUSION_ADAPT_STRUCT(
          client::ast_node,
          (std::string, text)
          (std::vector<client::ast_branch>, children)
    )
    
    namespace client
    {
        template <typename Iterator>
            struct ast_node_grammar : qi::grammar<Iterator, ast_branch(), ascii::space_type>
        {
            ast_node_grammar()
                : ast_node_grammar::base_type(brac)
            {
                using qi::_1;
                using qi::_val;
                using ascii::char_;
                using ascii::string;
    
                name %= *char_;
    
                brac = string("works");
                brac = string("works")[_val = _1] ;
                brac %= string("this") | string("works");
                brac %= name ; // works
                brac %= *char_ ; // works
    
                brac = qi::as_string[ string("no way") ];
                brac = qi::attr_cast( string("no way") );
            }
            qi::rule<Iterator, std::string()> name;
            qi::rule<Iterator, ast_branch(), ascii::space_type> brac;
        };
    }
    
    
    int main(int argc, char **argv)
    {
       typedef client::ast_node_grammar<std::string::const_iterator> ast_node_grammar;
       ast_node_grammar gram;
       client::ast_branch ast;
    
       std::string text("dummy");
       using boost::spirit::ascii::space;
       std::string::const_iterator iter = text.begin();
       std::string::const_iterator end = text.end();
       bool r = phrase_parse(iter, end, gram, space, ast);
       return r ? 0 : 1;
    }
    

    【讨论】:

    • assign_to_attribute_from_iterators 的提示很好。
    • @pdug 谢谢!欢迎来到Stack Overflow。记得vote/accept,这样人们就会知道哪些答案对 OP 有帮助
    • 我使用 dart 风格的谷歌查询找到了这个答案。这肯定是qi::string 生成std::string 的文档错误。虽然他们似乎知道这一点,并且为这种情况制作了 qi::as_string[...] 语法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-22
    • 1970-01-01
    相关资源
    最近更新 更多