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

Why can't I parse this double_?


When I parse an input as a std::string, I get the string, but when I parse this as a double_, the fusion struct contains some very small number rather than what is expected.

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

#include <string>

// http://www.boost.org/doc/libs/1_57_0/libs/spirit/example/qi/employee.cpp

namespace FormatConverter {
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    // TODO: should this have an initializer?
    struct asc {
        double timestamp;
    };
}

BOOST_FUSION_ADAPT_STRUCT(
    FormatConverter::asc,
      (double, timestamp)
)

namespace FormatConverter {

    template <typename Iterator>
    struct asc_parser : qi::grammar< Iterator, asc(), ascii::space_type >
    {
        asc_parser()
            : asc_parser::base_type(start) {
                timestamp %= qi::double_ ;
                start %= timestamp ;
                ;
        }
        qi::rule< Iterator, double, ascii::space_type > timestamp;
        qi::rule< Iterator, asc(), ascii::space_type > start;
    };
}

And I am testing this with:

#define BOOST_TEST_MODULE parser
#include <boost/test/included/unit_test.hpp>

#include "../FormatConverter/FormatConverter.h"

#include <string>

BOOST_AUTO_TEST_SUITE( TestSuite1 )
BOOST_AUTO_TEST_CASE( timestamp ) {
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    using iterator_type = std::string::iterator;
    using parser_type = FormatConverter::asc_parser<iterator_type>;

    parser_type grammar;
    FormatConverter::asc record;

    std::string str("161.096841 ");
    auto beg = str.begin();
    auto end = str.end();

    auto success = qi::phrase_parse(beg, end, grammar, ascii::space, record);
    BOOST_REQUIRE(success);
    BOOST_REQUIRE(beg==end);

    std::cout << "timestamp: " << boost::fusion::as_vector(record) << std::endl;
}
BOOST_AUTO_TEST_SUITE_END()

Solution

  • You missed a () in the attribute:

    qi::rule< Iterator, double, ascii::space_type > timestamp;
    

    should be

    qi::rule< Iterator, double(), ascii::space_type > timestamp;
    

    Because the params order in qi::rule can be arbitrary (except Iterator), internally the lib uses some traits to recognize which is attr, which is skipper, ...etc. The attr field has to be in the form of function-sig, i.e. synthesized(inherited), if you write double, it won't be recognized as an attr, so your timestamp rule will actually have unused_type instead of double as its attr, that means, record.timestamp won't be filled by the parser, it's uninitialized.