Search code examples
c++boost-spirit-x3

Boost Spirit X3: Extracting Data into x3::variant<...> is always empty


I would like to parse some input as either a long or an std::string if it is quoted. The reasonable solution to this is to use x3::variant<long, std::string> to store the data. Here is a sample program:

#include <iostream>
#include <string>

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>

namespace x3 = boost::spirit::x3;

const x3::rule<class number_tag, long> number = "number";
const auto number_def = x3::long_;
BOOST_SPIRIT_DEFINE(number);

const x3::rule<class string_tag, std::string> string = "string";
const auto string_def = x3::lexeme['"' >> *(x3::char_ - '"') >> '"'];
BOOST_SPIRIT_DEFINE(string);

using any_type = x3::variant<long, std::string>;
const x3::rule<class any_tag, any_type> any = "any";
const auto any_def = number | string;
BOOST_SPIRIT_DEFINE(any);

int main()
{
    const std::string src = "18";
    any_type result;
    auto iter = src.begin();
    bool success = x3::phrase_parse(iter, src.end(), any, x3::space, result);
    if (!success || iter != src.end())
        return 1;
    else
        std::cout << "Result: " << result << std::endl;
}

My expected result is:

Result: 18

However, the actual result is simply:

Result:

What am I doing wrong? Boost version is 1.61.


Solution

  • You cannot print a variant just like that. You have to pass it to a Visitor. For eg (No much error checking done for the conversions):

    struct Visitor
    {
      using result_type = long;
    
      result_type operator()(long v) const { return v; }
      result_type operator() (const std::string& v) { return std::atol(v.c_str()); }
    };
    

    And should be called from your code like:

        if (!success || iter != src.end()) {
            return 1;
        } else {
            Visitor v;
            std::cout << "Result: " << boost::apply_visitor(v, result) << std::endl;
        }