在boost :: spirit :: qi的语义动作中,boost :: phoenix :: bind和boost :: phoenix :: actors存在问题

时间:2014-08-25 21:06:21

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

我认为我在理解我的boost::spirit::qi解析器应该如何编写时遇到问题。我只想通过语义动作将匹配的子串传递给函数。为了大致模仿this boost tutorial,我提出了以下代码:

template<class Iterator> 
struct _Calculator : public qi::grammar<Iterator> {                                                                                               

    qi::rule<Iterator> number, result; //and a whole bunch of other ones too                                                            

    qi::rule<Iterator,std::string()> variable;                                                                                      

    Code& code;                                                                                                    
    Variables& variables;                                                                                                          

    _Calculator(Code& code_, Variables& variables_)
      : _Calculator::base_type(result),                                                                                                           
        code(code_),
        variables(variables_)                                                                                                                     
   {  
          number =
                lexeme[(qi::char_("1-9") >> +qi::char_("0-9"))[boost::phoenix::bind(&fPushIntCV, qi::_1, boost::ref(code), boost::ref(variables))]]
              | lexeme[("0x" >> +qi::char_("0-9a-fA-F"))      [boost::phoenix::bind(&fPushIntCV, qi::_1, boost::ref(code), boost::ref(variables))]]
              | lexeme[("0b" >> +qi::char_("0-1"))            [boost::phoenix::bind(&fPushIntCV, qi::_1, boost::ref(code), boost::ref(variables))]]
              | lexeme[("0" >>  +qi::char_("0-7"))            [boost::phoenix::bind(&fPushIntCV, qi::_1, boost::ref(code), boost::ref(variables))]]
          //some other junk
    }
};
typedef _Calculator<std::string::const_iterator> Calculator;

fPushIntCV的声明如下:

void fPushIntCV (vector<char> my_str, Code& c, Variables& v);

我收到此错误:

function_ptr.hpp:89: error: conversion from 'char' to non-scalar type 'std::vector<char, std::allocator<char> >' requested

当我将fPushIntCV的声明更改为如下所示:

void fPushIntCV (char my_str, Code& c, Variables& v);

我收到此错误:

function_ptr.hpp:89: error: cannot convert 'std::vector<char, std::allocator<char> >' to 'char' in argument passing

我认为qi::_1的属性正在发生变化,但如果我包含两个函数原型并且现在传递boost::phoenix::bind一些模糊的重载函数指针,我会得到未解析的引用:

error: no matching function for call to 'bind(<unresolved overloaded function type>, const boost::spirit::_1_type&, ...(我的...跟踪无关垃圾)

我知道这可能是一个非常简单的错误和一个非常简单的修复,但是我有一个很好的时间来理解这个咒语,以使提升魔法做到这一点。这种语义行为期待的函数原型是什么?

1 个答案:

答案 0 :(得分:3)

一些观察结果:

  • 您似乎没有使用船长,因此使用lexeme是多余的(请参阅Boost spirit skipper issues

  • 您想知道如何检测解析器表达式公开的属性类型:请参阅Detecting the parameter types in a Spirit semantic action

    这些类型用解析器指令记录,例如, as_string[(qi::char_("1-9") >> +qi::char_("0-9"))]会产生boost::fusion::vector2<char, std::vector<char> >,这会直接反映在GCC上的错误消息中:

    boost/phoenix/bind/detail/preprocessed/function_ptr_10.hpp|50 col 39| error: could not convert ‘a0’ from ‘boost::fusion::vector2<char, std::vector<char> >’ to ‘std::vector<char>’
    
  • 不希望混合和匹配库占位符/包装器,例如boost::refboost::phoenix::ref

  • 您似乎正在重塑整数解析;考虑使用qi::int_parser代替

  • 似乎缺少解析0的案例:)

假设您希望my_str只是反映包含数字基本前缀的输入字符串,我建议使用:

number =
     as_string[(qi::char_("1-9") >> +qi::char_("0-9"))] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
   | as_string[("0x" >> +qi::char_("0-9a-fA-F"))      ] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
   | as_string[("0b" >> +qi::char_("0-1"))            ] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
   | as_string[("0" >>  +qi::char_("0-7"))            ] [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
   //some other junk
   ;

但是,这可以简化为:

number = as_string[
       (qi::char_("1-9") >> +qi::char_("0-9"))
     | ("0x" >> +qi::char_("0-9a-fA-F"))
     | ("0b" >> +qi::char_("01"))
     | ("0"  >> +qi::char_("0-7"))
   ]        [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
   ;

现在,您可能只想解析一个整数值:

number = 
     ( 
       ("0x" >> qi::int_parser<int, 16, 1>())
     | ("0b" >> qi::int_parser<int,  2, 1>())
     | ("0"  >> qi::int_parser<int,  8, 1>())
     | qi::int_ /* accepts "0" */) [phx::bind(&fPushIntCV, qi::_1, phx::ref(code), phx::ref(variables))]
   ;

转换 [1] 非常出色,您可以选择int

void fPushIntCV (int my_number, Code& c, Variables& v) {
    std::cout << "fPushIntCV: " << my_number << "\n";
}

[1] (还有uint_parser,您可以解析longlong long等等;甚至像boost::multiprecision::cpp_int这样的大整数应该没问题)

这是一个使用它的演示程序,显示值正确转换(并且:&#34; 0&#34;被接受:) :): Live On Coliru < / p>

int main()
{
    Code code;
    Variables variables;
    Calculator g(code, variables); 

    for (std::string const input : { "0", "0xef1A", "010", "0b10101" })
    {
        It f(input.begin()), l(input.end());

        if(qi::parse(f, l, g))
            std::cout << "Parse success ('" << input << "')\n";
        else std::cout << "Parse failed ('" << input << "')\n";

        if (f != l)
            std::cout << "Input remaining: '" << std::string(f, l) << "'\n";
    }
}

打印

fPushIntCV: 0
Parse success ('0')

fPushIntCV: 61210
Parse success ('0xef1A')

fPushIntCV: 8
Parse success ('010')

fPushIntCV: 21
Parse success ('0b10101')