Search code examples
c++parsingcompiler-errorsboost-spirit-qi

boost::spirit::qi compiler error: cannot convert 'const some_stuff::return_type' to 'unsigned int' without a conversion operator


I'm facing with a compilation error in a program I'm writing using boost1.54 and clang++-3.9. Consider the following (simplified) example code:

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>

using namespace boost::spirit;

struct some_stuff {
    struct return_type {
        unsigned int field_1;
    };

    struct id : qi::grammar<std::string::iterator>{
        id() : id::base_type(rule_){ rule_ = qi::lit("?AL"); }
    private:
        qi::rule<std::string::iterator> rule_;
    };

    struct payload : qi::grammar<std::string::iterator, return_type()> {
        payload() : payload::base_type{rule_} { rule_ = qi::uint_; }
    private:
        qi::rule<std::string::iterator, return_type()> rule_;
    };
};
BOOST_FUSION_ADAPT_STRUCT(some_stuff::return_type, (unsigned int, field_1))

struct my_grammar : qi::grammar<std::string::iterator, some_stuff::return_type()>{
    my_grammar() : my_grammar::base_type(rule_){ rule_ = "+++AT" >> i_ >> ':' >> qi::omit[qi::uint_] >> ':' >> p_; }
private:
    some_stuff::id i_;
    some_stuff::payload p_;
    qi::rule<std::string::iterator, some_stuff::return_type()> rule_;
};

int main() {
    std::string s("+++AT?AL:1:3,5");
    my_grammar g_;
    some_stuff::return_type v_;

    auto it = s.begin();
    if (qi::phrase_parse(it, s.end(), g_, ascii::space, v_)) {
        std::cout << "Field 1: " << v_.field_1 << std::endl;
    }
    return 0;
}

When I try to compile it, I get the error reported in the title:

error: cannot convert 'const some_stuff::return_type' to 'unsigned int' without a conversion operator 

However, if I add the cast operator to unsigned int in the struct some_stuff::return_type everything works fine. Can someone help me to understand why the cast operator is mandatory in this case or, in case it is not, how to solve this issue?


Solution

  • You've been bit by the infamous "single-element sequence" bug/limitation: Spirit Qi attribute propagation issue with single-member struct

    Adding a dummy field to return_type make it work (also added debugging):

    Live On Coliru

    #define BOOST_SPIRIT_DEBUG
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <iostream>
    #include <string>
    
    using namespace boost::spirit;
    
    struct some_stuff {
        struct return_type {
            unsigned int field_1;
            bool dummy;
        };
    
        struct id : qi::grammar<std::string::iterator> {
            id() : id::base_type(rule_) {
                rule_ = qi::lit("?AL"); 
                BOOST_SPIRIT_DEBUG_NODES((rule_))
            }
    
          private:
            qi::rule<std::string::iterator> rule_;
        };
    
        struct payload : qi::grammar<std::string::iterator, return_type()> {
            payload() : payload::base_type{ rule_ } {
                rule_ = qi::uint_ >> qi::attr(false); 
                BOOST_SPIRIT_DEBUG_NODES((rule_))
            }
    
          private:
            qi::rule<std::string::iterator, return_type()> rule_;
        };
    };
    BOOST_FUSION_ADAPT_STRUCT(some_stuff::return_type, field_1, dummy)
    
    struct my_grammar : qi::grammar<std::string::iterator, some_stuff::return_type()> {
        my_grammar() : my_grammar::base_type(rule_) {
            rule_ = "+++AT" >> i_ >> ':' >> qi::omit[qi::uint_] >> ':' >> p_; 
            BOOST_SPIRIT_DEBUG_NODES((rule_))
        }
    
      private:
        some_stuff::id i_;
        some_stuff::payload p_;
        qi::rule<std::string::iterator, some_stuff::return_type()> rule_;
    };
    
    int main() {
        std::string s("+++AT?AL:1:3,5");
        my_grammar g_;
        some_stuff::return_type v_;
    
        auto it = s.begin();
        if (qi::phrase_parse(it, s.end(), g_, ascii::space, v_)) {
            std::cout << "Field 1: " << v_.field_1 << std::endl;
        }
    }
    

    Prints

    <rule_>
      <try>+++AT?AL:1:3,5</try>
      <rule_>
        <try>?AL:1:3,5</try>
        <success>:1:3,5</success>
        <attributes>[]</attributes>
      </rule_>
      <rule_>
        <try>3,5</try>
        <success>,5</success>
        <attributes>[[3, 0]]</attributes>
      </rule_>
      <success>,5</success>
      <attributes>[[3, 0]]</attributes>
    </rule_>
    Field 1: 3
    

    NOTE

    You're using phrase_parse without a skipper. That's not working, and not what you want: Boost spirit skipper issues