Search code examples
c++boostboost-spirit-qi

boost spirit grammar error - "no type named ‘size’ in ‘struct boost::spirit::unused_type’"


Could please help me with diagnosing the following error. I have a simple grammar:

struct json_start_elem_grammar_object : qi::grammar<StreamIterator,
                                                  void(const CharType*, CharType),
                                                  ascii::space_type>
{
  json_start_elem_grammar_object() : json_start_elem_grammar_object::base_type(start_elem, "start_elem")
  {
    start_elem =  qi::lit('"') > qi::lit(qi::_1) > qi::lit('"') > qi::lit(':') >
                  qi::lit(qi::_2) > -qi::lit('\n');
  }

  qi::rule<StreamIterator, void(const CharType*, CharType), ascii::space_type>  start_elem;
};

when making an instance of this grammar I get an error:

/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:220:19:   required from ‘boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>& boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::operator=(const Expr&) [with Expr = boost::proto::exprns_::expr<boost::proto::tagns_::tag::greater, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::greater, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::greater, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::greater, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::greater, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::terminal_ex<boost::spirit::tag::lit, boost::fusion::vector1<char> > >, 0l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::lazy_terminal<boost::spirit::tag::lit, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::detail::function_eval<1>, boost::fusion::vector<boost::phoenix::value<boost::spirit::terminal<boost::spirit::tag::lit> >, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >, 1> >, 0l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::terminal_ex<boost::spirit::tag::lit, boost::fusion::vector1<char> > >, 0l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::terminal_ex<boost::spirit::tag::lit, boost::fusion::vector1<char> > >, 0l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::lazy_terminal<boost::spirit::tag::lit, boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::detail::function_eval<1>, boost::fusion::vector<boost::phoenix::value<boost::spirit::terminal<boost::spirit::tag::lit> >, boost::spirit::argument<1>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >, 1> >, 0l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::negate, boost::proto::argsns_::list1<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::terminal_ex<boost::spirit::tag::lit, boost::fusion::vector1<char> > >, 0l>&>, 1l>&>, 2l>; Iterator = boost::spirit::basic_istream_iterator<char>; T1 = void(const char*, char); T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’
/home/marcin/workspace/json_archive/basic_json_grammar.hpp:149:18:   required from ‘boost::archive::basic_json_grammar<CharType>::json_start_elem_grammar_object::json_start_elem_grammar_object() [with CharType = char]’
/home/marcin/workspace/json_archive/basic_json_grammar.hpp:194:50:   required from ‘boost::archive::basic_json_grammar<CharType>::basic_json_grammar() [with CharType = char]’
/home/marcin/workspace/json_archive/json_iarchive_impl.ipp:141:85:   required from ‘boost::archive::json_iarchive_impl<Archive>::json_iarchive_impl(std::istream&, unsigned int) [with Archive = boost::archive::naked_json_iarchive; std::istream = std::basic_istream<char>]’
/home/marcin/workspace/json_archive/json_iarchive.hpp:102:68:   required from here
/usr/include/boost/fusion/sequence/intrinsic/size.hpp:33:20: error: no type named ‘size’ in ‘struct boost::spirit::unused_type’
         struct unsegmented_size : Sequence::size {};

where

using CharType = char;
using StreamIterator = spirit::basic_istream_iterator<CharType>;

After few days of working with boost.serialization & boost.spirit compilation errors I'm totally brain-dead and seem to be stuck on this one :/

The usage of this grammar (slightly simplified) is:

json_start_elem_grammar_object start_elem_parser_object;
using invoker = _details::invoke_grammar<CharType, decltype(start_elem_parser_object(name, preamble))>;

invoker::apply( is,
              start_elem_parser_object(name, preamble),
              "Invalid object element in archive");

where

decltype(is) = IStream& 
decltype(name) = const CharType* 
decltype(preamble) = CharType 


template<typename CharType, typename Expr>
struct invoke_grammar<CharType, Expr> {
 using IStream = std::basic_istream<CharType>;
 using StreamIterator = spirit::basic_istream_iterator<CharType>;

 static void apply(IStream & is, Expr const& grammar, const CharType* errMsg)
 {
   boost::io::ios_flags_saver ifs(is);
   is.unsetf (std::ios::skipws);

   StreamIterator it_end;
   StreamIterator it_beg (is);

   if (!qi::phrase_parse(it_beg, it_end,
                        grammar,
                        ascii::space))
   {
     boost::serialization::throw_exception(
       archive_exception(archive_exception::invalid_signature, errMsg)
     );
   }
 }
};

Solution

  • Like I commented, you need to use qi::_r1 and qi::_r2, the placeholders for inherited attributes[1].

    For the rest I can assume things (as your code is incomplete and slightly unclear to me due to a non-standard approach):

    • you probably want ascii::blank_type as the skipper, since otherwise lit('\n') would never match (unless with no_skip[] or lexeme[]).
    • you have to take care to call the parser with an actual CharType const*, not a CharType const (&)[]. The latter happens if you call it with e.g. parser("name", 'a'). Instead, either

      • introduce a temporary variable
      • a cast
      • implicitely decay using e.g. +"name"

    I've made up an example and it succeeds, see it Live on Coliru

    Full code:

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/include/qi.hpp>
    
    namespace qi    = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    
    template <typename StreamIterator, typename CharType=char>
    struct json_start_elem_grammar_object : qi::grammar<StreamIterator,
        void(const CharType*, CharType),
        ascii::blank_type>
    {
        json_start_elem_grammar_object() : json_start_elem_grammar_object::base_type(start_elem, "start_elem")
        {
            using namespace qi;
            _r1_type _name;     // prefer descriptive names
            _r2_type _preamble;
    
            start_elem = 
                '"' > lit(_name) > '"' > 
                ':' > lit(_preamble) > 
                -lit('\n')
                ;
        }
    
        qi::rule<StreamIterator, void(const CharType*, CharType), ascii::blank_type>  start_elem;
    };
    
    bool doParse(const std::string& input)
    {
        typedef std::string::const_iterator It;
        auto f(begin(input)), l(end(input));
    
        json_start_elem_grammar_object<It> p;
    
        try
        {
            bool ok = qi::phrase_parse(f, l, p(+"lol", 'q'), ascii::blank);
            if (ok)   
            {
                std::cout << "parse success\n";
            }
            else      std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
    
            if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
            return ok;
        } catch(const qi::expectation_failure<It>& e)
        {
            std::string frag(e.first, e.last);
            std::cerr << e.what() << "'" << frag << "'\n";
        }
    
        return false;
    }
    
    int main()
    {
        bool ok = doParse("\"lol\" : q\n");
        return ok? 0 : 255;
    }
    

    [1] See also