Parser attribute

Each Boost Spirit parser has an attribute. The attribute of a simple parser is easy to spot: for the built-in double_ is variable of double type.

The attribute of a list parser is a std::vector whose underlying type is determined by the attribute type of its elements. So the std::vector<double> is the attribute of
double_ % ','
The interesting consequence is that we can use attributes instead of semantic actions to work on the sequence elements, like we are going to see here below.

The problem

We have a CSV list of floating point numbers in a string, we want to generate from it a std::vector<double> containing its values.

Solution by parser semantic action

Combining the usage of semantic action (implemented by a standard lambda function) with the Boost Spirit shortcut syntax for list we should easily get code like this:
bool parse2vector(const std::string& input, std::vector<double>& vd)
{
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::unused_type;
using boost::spirit::qi::double_;
using boost::spirit::qi::ascii::space;

auto pushBack = [&vd](double d, unused_type, unused_type){ vd.push_back(d); };

std::string::const_iterator beg = input.begin();
bool ret = phrase_parse(beg, input.end(), double_[ pushBack ] % ',', space);
return beg != input.end() ? false : ret;
}

Piece of cake. For each double_ element found by the parser in the input string the semantic action pushBack is called. The semantic action is a lambda function that has reference access to the vector we want to fill in, and receive as input parameter the double value as parsed by the parser (and other two parameters not used here); then it simply push back in the vector the value.

Could there be anything simpler than that? Actually, yes, there is.

Solution by parser attribute

We could rewrite the same function passing to phrase_parse() an extra parameter, that should match the parser type. As we said above, a list of double_ has a std::vector<double> as attribute:
bool parse2vector(const std::string& input, std::vector<double>& vd)
{
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::double_;
using boost::spirit::qi::ascii::space;

std::string::const_iterator beg = input.begin();
bool ret = phrase_parse(beg, input.end(), double_ % ',', space, vd);
return beg != input.end() ? false : ret;
}

No more action specified, we let Spirit doing the dirty job for us.

I based this post on a C++ source file provided by the original Boost Spirit Qi documentation.

No comments:

Post a Comment