boost::variant implicit cast to string

2.1k views Asked by At

I have a boost::variant with different types, where one is a (const) void pointer and another a string.

boost::variant<std::string, void const*>;

The problem is, if i want to use it with a c-string it casts it to the void pointer instead the string.

boost::variant<std::string, void const*> foo;
foo = "bar";
std::cout << foo.which(); // prints 1 -> void const*

If I remove the constness of the pointer it will cast it to the string.

boost::variant<std::string, void*> foo; // no const pointer
foo = "bar";
std::cout << foo.which(); // prints 0 -> string

Is there an easy way to make boost::variant implicitly cast the c-string to a std::string?

UPDATE

I know I can cast explicit with:

foo = std::string("bar");

But I would like to avoid the explicit casting.

4

There are 4 answers

4
m.s. On BEST ANSWER

You can provide your own variant template by inheriting from boost::variant. The correct conversion from const char* to std::string is achieved through overloading the operator=:

#include <iostream>
#include <string>
#include <boost/variant.hpp>

template <typename... Types>
struct my_variant : public boost::variant<Types...>
{
    using boost::variant<Types...>::variant;

    auto operator=(const char* rhs)
    {
        return boost::variant<Types...>::operator=(std::string(rhs));
    }
};

int main() 
{
    my_variant<std::string, void const*> foo;
    foo = "bar";
    std::cout << foo.which(); 
    return 0;
}

Outputs "0" as desired.

Live example: https://ideone.com/ZppUla

This idea could be even more generalized through the use of a traits class which specifies the type mapping:

template <template <typename> class conv_traits, typename... Types>
struct my_variant : public boost::variant<Types...>
{
    using boost::variant<Types...>::variant;

    template <typename T>
    auto operator=(T rhs)
    {
        return boost::variant<Types...>::operator=(static_cast<typename conv_traits<T>::type>(rhs));
    }
};

template <typename T>
struct conversion_traits
{
    typedef T type;
};

template <>
struct conversion_traits<const char*>
{
    typedef std::string type;
};

my_variant<conversion_traits, std::string, void const*> foo;

Live example: https://ideone.com/AXUqTv

0
Andre On

You can derive your own class from boost::variant<std::string, void const*> and provide implicit const char* and string constructors.

0
AudioBubble On

There's not a way to do this directly to my knowledge, but you could solve it with a wrapper. Basic one (to be embellished as you want and generalized as much as you want):

struct VariantWrapper
{
    template <class T>
    VariantWrapper& operator=(const T& val)
    {
        var = val;
        return *this;
    }

    template <int N>
    VariantWrapper& operator=(const char(&str)[N])
    {
        var = std::string(str);
        return *this;
    }

    boost::variant<std::string, void const*> var;
};

VariantWrapper bar;
bar = "foo";
std::cout << bar.var.which();

Doing it the above way will restrict the template matching to just literal strings and character arrays but still allow you to do something different with, say, const char*. Though you could just use const char* if you want to match everything that fits into the c-string category.

0
Spin16 On

It can be solved with xnode (class similar to boost::variant in some ways, similar to boost::any in other ways):

using namespace std;
xnode value = xnode::value_of("bar");
cout << "type: " << value.type().name() << ", value: " << value.get_as<string>() << endl;

There is explicit conversion path inside this class for string literals (arrays).

See: https://github.com/vpiotr/xnode