2

Suppose I have the following string to parse:

"1.2, 2.0, 3.9"

and when I apply the following parser for it:

struct DataStruct
{
    double n1, n2, n3;
};

BOOST_FUSION_ADAPT_STRUCT(DataStruct, (double, n1)(double, n2)(double, n3))

qi::rule<std::string::iterator, DataStruct()> data_ =
                                                      qi::double_ >> ','
                                                   >> qi::double_ >> ','
                                                   >> qi::double_;

auto str = "1.2, 2.0, 3.9";
auto it - str.begin();
if (qi::parse(it, str.end(), data_, res))
{
    std::cout << "parse completed" << std::endl;
}

everything is ok, but when I suppose that instead of some double in my string I can get "null" (i.e. "1.2, null, 3.9") I want to assign 0 value to appropriate double value in DataStruct. Is any way to do this ?

bladzio
  • 414
  • 3
  • 15

2 Answers2

3

The usual trick is to use an alternative with qi::attr:

rule_def = parser_expression | qi::attr(default_value);

In your case, perhaps:

reader_ = qi::double_ | qi::lit("null") >> qi::attr(0);

Demo

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
struct DataStruct { double n1, n2, n3; };

BOOST_FUSION_ADAPT_STRUCT(DataStruct, n1, n2, n3)

namespace qi = boost::spirit::qi;

int main() {
    using Iterator = typename std::string::const_iterator;

    qi::rule<Iterator, double()> reader_   = qi::double_ | qi::lit("null") >> qi::attr(0);
    qi::rule<Iterator, DataStruct()> data_ = reader_ >> ',' >> reader_ >> ',' >> reader_;

    DataStruct res;
    auto const str = std::string("1.2,null,3.9");
    Iterator start = str.begin(), end = str.end();

    if (qi::parse(start, end, data_ >> qi::eoi, res)) {
        std::cout << "parsed: " << boost::fusion::as_vector(res) << "\n";
    }
    else {
        std::cout << "parse failed\n";
    }
}

Prints

parsed: (1.2 0 3.9)

Note the review changes (don't using namespace, check for eoi).

sehe
  • 374,641
  • 47
  • 450
  • 633
1

You might use the following code:

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
struct DataStruct
{
    double n1, n2, n3;
};

BOOST_FUSION_ADAPT_STRUCT(DataStruct, (double, n1)(double, n2)(double, n3))

int main() {
    using namespace boost::spirit;

    using Iterator = typename std::string::iterator;

    qi::rule<Iterator, double()> reader_ = (qi::double_[_val = _1] | (lexeme["null"])[_val = 0.]);

    qi::rule<Iterator, DataStruct()> data_ = reader_ >> ',' >> reader_ >> ',' >> reader_;

    try {
        DataStruct res;
        auto str = std::string("1.2,null,3.9");
        auto start = str.begin();
        auto end = str.end();

        bool ok = qi::parse(start, end, data_, res);
        if (ok) {
            std::cout << "parse completed" << std::endl;
        }
        else {
            std::cout << "parse failed" << std::endl;
        }
    }
    catch (const qi::expectation_failure<std::string::iterator>& except) {
        std::cout << except.what();
    }

    std::cout << std::endl;
}
Aleph0
  • 5,816
  • 4
  • 29
  • 80
  • This seems like an overly complicated approach ([Semantic Actions Are Evil](http://stackoverflow.com/questions/8259440/boost-spirit-semantic-actions-are-evil)). Also, `lexeme` is not the right tool there (the rule is implicitly lexeme anyways) – sehe Mar 16 '17 at 15:10