Reputation: 5334
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?
I know I can cast explicit with:
foo = std::string("bar");
But I would like to avoid the explicit casting.
Upvotes: 3
Views: 2192
Reputation: 1
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
Upvotes: 0
Reputation: 16334
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
Upvotes: 4
Reputation:
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.
Upvotes: 0
Reputation: 647
You can derive your own class from boost::variant<std::string, void const*>
and provide implicit const char* and string constructors.
Upvotes: 0