0

I am trying (and failing) to parse a map<int, variant<string, float>> using Boost Spirit X3, with the following code:

#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <map>
#include <variant>
#include <string>

using namespace std;

namespace x3 = boost::spirit::x3;

int main() {
    auto variantRule = x3::rule<class VariantClass, x3::variant<std::string, float>>() = (*x3::alnum | x3::float_);

    auto pairRule = x3::rule<class PairClass, pair<int, x3::variant<std::string, float>>>() = x3::int_ >> ":" >> variantRule;

    auto mapRule = x3::rule<class MapClass, map<int, x3::variant<std::string, float>>>() = pairRule >>  * ( "," >> pairRule );

    string input = "1 : 1.0, 2 : hello, 3 : world";

    map<int, x3::variant<std::string, float>> variantMap;

    auto success = x3::phrase_parse(input.begin(), input.end(), mapRule, x3::space, variantMap);

    return 0;
}

For some reason I can't parse maps of pair<int, variant<string, float>>. I was able to parse a vector of variants though, it's only when I try to parse maps of variants that my code fails. It's worth mentioning that I also went through the X3 tutorials. Any help would be greatly appreciated.

Edit 1

Taking into account @liliscent's answer, and a few other changes, I was finally able to get it working, here's the correct code:

#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <map>
#include <variant>
#include <string>

using namespace std;

namespace x3 = boost::spirit::x3;

int main() {
    auto stringRule = x3::rule<class StringClass, string>() = x3::lexeme[x3::alpha >> *x3::alnum];

    auto variantRule = x3::rule<class VariantClass, x3::variant<std::string, float>>() = (stringRule | x3::float_);

    auto pairRule = x3::rule<class PairClass, pair<int, x3::variant<std::string, float>>>() = x3::int_ >> ':' >> variantRule;

    auto mapRule = x3::rule<class MapClass, map<int, x3::variant<std::string, float>>>() = pairRule % ",";

    string input = "1 : 1.0, 2 : hello, 3 : world";

    map<int, x3::variant<std::string, float>> variantMap;

    auto bg = input.begin(), ed = input.end();

    auto success = x3::phrase_parse(bg, ed, mapRule, x3::space, variantMap) && bg == ed;

    if (!success) cout<<"Parsing not succesfull";

    return 0;
}
llllllllll
  • 16,169
  • 4
  • 31
  • 54
Jaime Ivan Cervantes
  • 3,579
  • 1
  • 40
  • 38

1 Answers1

3

If you want spirit to recognize std::pair and std::map, you need to include the fusion adaptor for std::pair:

#include <boost/fusion/adapted/std_pair.hpp>

This should fix your compilation problem. But there are other problems in your code, this rule (*x3::alnum | x3::float_); won't do what you want, because the left part could directly match empty. You need to rethink how to define this identifier.

Also, better to write pairRule % "," than pairRule >> * ( "," >> pairRule );.

And you should pass the input begin as an lvalue iterator, because then it will be advanced during parsing, so that you can check whether the parser has prematurely terminated.

llllllllll
  • 16,169
  • 4
  • 31
  • 54
  • 1
    Alternatively, add `>> x3::eoi` at the end to ensure parsing fails if the full input hasn't been consumed – sehe Dec 28 '18 at 11:09
  • I found fusion's `std_pair` issue on stack overflow: https://stackoverflow.com/questions/4874075/parsing-a-pair-of-ints-with-boost-spirit/4874297#4874297. With that `include`, your suggestions, and a few other changes I was finally able to get it work. See updated answer. – Jaime Ivan Cervantes Dec 28 '18 at 18:31
  • I'm still trying to figure out why if I use `pairRule >> *(',' >> pairRule)` instead of `pairRule % ','` I get a compilation error: `error: no matching function for call to 'std::map<...>::insert(...)`. Shouldn't both rules work in the same way? – Jaime Ivan Cervantes Dec 28 '18 at 20:13
  • @JaimeIvanCervantes It should. Take a look at the online compiler link in my answer, your original code compiles just by adding the fusion adaptor include. If you still get error for your new code, I suggest you ask another question. – llllllllll Dec 29 '18 at 09:30