C++ boost::variant generic converter

165 views Asked by At

I've been trying for the last three day to figure out how to implement a generic way of getting the value out of a boost::variant<...>, but it's been quite difficult.

Here is the solution I could come up with, which it not at all generic:

#include <iostream>
#include "boost\variant\variant.hpp"

using MyVariant = boost::variant<int, std::string>;

class VariantConverter : public boost::static_visitor<>
{
private:
    mutable int _int;
    mutable std::string _string;

    static VariantConverter apply(MyVariant& v) 
    {
        VariantConverter res;

        v.apply_visitor(res);

        return res; // copy will be elided, right?
    }

public:
    void operator()(int& i) const
    {
        _int = i;
    }

    void operator() (std::string& s) const
    {
        _string = s;
    }

    static int to_int(MyVariant v) 
    {
        return apply(v).from_int();
    }

    static std::string to_string(MyVariant v) 
    {
        return apply(v).from_string();
    }

    int from_int() 
    { 
        return _int; 
    };

    std::string from_string() 
    { 
        return _string; 
    };
};

int main()
{
    using namespace std;

    MyVariant v = 23;

    int i = VariantConverter::to_int(v);

    cout << i << endl;

    v = "Michael Jordan";

    std::string s = VariantConverter::to_string(v);

    cout << s.c_str() << endl;

    cin.get();

    return 0;
}

I'd appreciate it if someone could guide me towards a better solution.

Or perhaps someone could explain to me the rationale behind this:

if I declare a:

using MyVariant = boost::variant<int, std::string>;

and then a:

ConverterToInt : basic_visitor<int> {
public:
    int operator() (int i) { return i; };
};

Why is it that when I try to apply the ConverterToInt to a MyVariant as such:

ConverterToInt cti;

MyVariant i = 10;

i.apply_visitor(cti);

I get a compiler error about trying to find a operator() that takes a std::string?

It seems to me that apply_visitor is trying to call an operator() for each of the types MyVariant can take. Is that so? If it is, why? How can i avoid this behavior?

Cheers!

2

There are 2 answers

0
rici On

You can avoid the error message by telling ConverterToInt what to do with a std::string. You might know that i can't be a std::string but it's unreasonable to expect the compiler to know that (and if it is true, why are you using a variant?).

apply_visitor will only call the correct operator() method, but it decides at run time, and the compiler needs to have all the possibilities covered to generate the code.

0
Jeffrey Faust On
MyVariant iv = 10;    
int i = boost::get<int>(iv);

boost::variant does not "call" each operator() of an interface when invoked, but it must be able to. That's the entire point. A variant can hold any of the template types, so if you want to define an operation on it, you must specify somewhere what that operation means for each type.