Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change attribute type when parsing binary with boost::spirit

I have been successfully using boost::spirit::qi to to parse a stream consisting of the built-in parsers (e.g. byte_, little_word, etc). However, I now need to parse data that doesn't neatly fall into one of these categories. For example, I would like to convert a 16.16 fixed-point binary number into a double; e.g. so little_word << little_16p16 would parse a uint16_t followed by a double (parses from a fixed-point number).

I first considered semantic actions, but (I think...) that they are not appropriate because they do not change the type of the attribute associated with a parser. I also cannot figure out how to adapt the employee struct-parsing example to this situation because it relies on implicit casts provided by boost::fusion. That approach won't work here because I obviously can't define an implicit cast from uint32_t to double without causing major problems.

My inclination is that I need to add non-terminals to wrap the built-in binary primitive parsers or write a terminal parser from scratch. Even after looking at the source of qi_binary.hpp, I am not sure how to do either. Could anyone provide some sample code and/or direct me to the relevant references to get started?

like image 333
Michael Koval Avatar asked Mar 15 '12 07:03

Michael Koval


1 Answers

    template < typename Iterator >
    struct parser : boost::spirit::qi::grammar < Iterator, double(), boost::spirit::ascii::space_type >
    {
        struct cast_impl
        {
            template < typename A >
            struct result { typedef double type; };

            double operator()(boost::fusion::vector < boost::uint16_t, boost::uint16_t > arg) const
            {
                // cast here
                return 0;
            }
        };

        parser() : parser::base_type(main)
        {
            pair = boost::spirit::qi::little_word >> '.' >> boost::spirit::qi::little_word;
            main = pair[boost::spirit::qi::_val = cast(boost::spirit::qi::_1)];
        }

        boost::spirit::qi::rule < Iterator, boost::fusion::vector < boost::uint16_t, boost::uint16_t > (), boost::spirit::ascii::space_type > pair;
        boost::spirit::qi::rule < Iterator, double(), boost::spirit::ascii::space_type > main;

        boost::phoenix::function<cast_impl> cast;
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
        typedef std::string container;

        container data_ = "\x01\x02.\x01\x02";

        container::iterator iterator_ = data_.begin();

        double value_;

        bool result_ =
            boost::spirit::qi::phrase_parse(iterator_, data_.end(),
            parser < container::iterator > (),
            boost::spirit::ascii::space,
            value_);

        return 0;
    }
like image 120
sliser Avatar answered Nov 16 '22 06:11

sliser