Search code examples
c++parsingboostboost-spiritboost-spirit-qi

Support BOOST_FUSION_ADAPT_STRUCT for objects with fixed arrays?


Assume I already have a struct that looks like this:

struct LETTER
{
    double one;
    char[12] two;
    double three;
    char[12] four;
};

And my inputs are comma separated, for example:

"32,CATSANDDOGS,42,WHAT"
"43,BATANDZEBRAS,23,PARROT"

I've been trying to adapt this example (Spirit Qi : rule for char [5]) to to roll through BOOST_FUSION_ADAPT_STRUCT but haven't had any luck. I tried using std::array as shown here (http://www.boost.org/doc/libs/1_64_0/libs/spirit/example/qi/boost_array.cpp) but I haven't been able to make it work in a struct. Is what I'm trying to do even possible? What am I doing wrong here? I would think this would be the most obvious use case.

Is what I'm trying to do even possible?


Solution

  • I'm going to assume you want to write idiomatic C++ code (which is obviously the target domain for Spirit Qi), so you can use std::string:

    Live On Coliru

    #include <boost/fusion/adapted.hpp>
    #include <boost/fusion/include/io.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <iostream>
    
    namespace qi = boost::spirit::qi;
    
    struct Letter {
        double one;
        std::string two;
        double three;
        std::string four;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(Letter, one, two, three, four)
    
    template <typename Iterator> struct LETTERParser : qi::grammar<Iterator, Letter()> {
        LETTERParser() : LETTERParser::base_type(start) {
            using namespace qi;
    
            _11c = repeat(11) [char_];
            start = skip(space) [ "LETTER" >> double_ >> _11c >> double_ >> _11c ];
        }
      private:
        qi::rule<Iterator, Letter()> start;
        qi::rule<Iterator, std::string()> _11c;
    };
    
    int main() {
        const std::string input("LETTER 42 12345678901 +Inf abcdefghijk  ");
        using It = std::string::const_iterator;
    
        LETTERParser<It> parser;
        Letter example;
    
        It f = input.begin(), l = input.end();
    
        if (phrase_parse(f, l, parser, qi::ascii::space, example)) {
            std::cout << "parsed: " << boost::fusion::as_vector(example) << "\n";
            std::cout << " example.one: " << example.one << "\n";
            std::cout << " example.two: '" << example.two << "'\n";
            std::cout << " example.three: " << example.three << "\n";
            std::cout << " example.four: '" << example.four << "'\n";
        } else {
            std::cout << "couldn't parse '" << input << "'\n";
        }
    
        if (f != l)
            std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
    }
    

    Prints

    parsed: (42 12345678901 inf abcdefghijk)
     example.one: 42
     example.two: '12345678901'
     example.three: inf
     example.four: 'abcdefghijk'