0

I'm trying to make this code work with an added print call, but this program produces a stack overflow:

#include <iostream>
#include <variant>

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

auto const unquoted_text_field = *(boost::spirit::x3::char_ - ',' - boost::spirit::x3::eol);

struct text { };
struct integer { };
struct real { };
struct skip { };
typedef std::variant<text, integer, real, skip> column_variant;

std::ostream& operator<< (std::ostream& os, column_variant const& v) {
    std::visit([&os](auto const& e) { os << e; }, v);
    return os;
}

struct column_value_parser : boost::spirit::x3::parser<column_value_parser> {
    typedef boost::spirit::unused_type attribute_type;

    std::vector<column_variant>& columns;
    size_t mutable pos = 0;
    struct pos_tag;

    column_value_parser(std::vector<column_variant>& columns)
        : columns(columns)
    { }

    template<typename It, typename Ctx, typename Other, typename Attr>
    bool parse(It& f, It l, Ctx& /*ctx*/, Other const& /*other*/, Attr& /*attr*/) const {

        std::cout << columns[pos] << std::endl;
        return true;
    }
};

int main() {

    std::string input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
    std::vector<column_variant> columns = { text{}, integer{}, real{}, skip{} };

    auto at = input.begin();
    boost::spirit::x3::parse(at, input.end(),
        (column_value_parser(columns) % ',') % boost::spirit::x3::eol);
}

And that makes sense, it get buried in a recursive call to the << operator. So, how does the guy in this link make it work?

Barry
  • 286,269
  • 29
  • 621
  • 977
lakeweb
  • 1,859
  • 2
  • 16
  • 21

1 Answers1

3

Here's a way reduced example that doesn't need Boost.Spirit (and thus compiles much faster):

#include <iostream>
#include <variant>

struct text { };
typedef std::variant<text> column_variant;

std::ostream& operator<< (std::ostream& os, column_variant const& v) {
    std::visit([&os](auto const& e) { os << e; }, v); 
    return os; 
}

int main() {
    column_variant v;
    std::cout << v;
}

So the first question I had when I saw your code is actually: how does this even compile? I expected it not to, and was surprised when it did. And the reason it compiles is actually the same reason that you get the stack overflow, which is...

What does this actually do?

std::cout << text{};

There's no operator<< taking a text, right? But there is an operator<< taking an argument that can be constructed from a text: column_variant! Because none of the types in your variant are independently streamable, trying to stream any of them leads to recursively invoking the variant stream operator.

Stick in something like this:

std::ostream& operator<<(std::ostream& os, text) {
    return os << "text";
}

And no more recursion.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks Barry. Head slap, can't see the forest... I'm using types and expecting the compiler to dig out the std::string, int, etc.. Of course it won't, I needed to finish the overloading. – lakeweb Sep 13 '18 at 16:57