This seems like it may be impossible, but I thought I would ask anyway.
I have defined a boost::variant
like this:
typedef boost::variant<double, int, std::string> ConfigVariant;
Later in my code I define a std::map
like this:
std::map<std::string, ConfigVariant> my_map;
Now I would like to be able to have std::map<std::string, ConfigVariant>
values inside my_map
. For example, I would like to do this:
my_map[key1][key2] = "hello world";
The reason I think this is impossible is because it seems like the corresponding variant definition would look like this:
typedef boost::variant<double, int, std::string, std::map<std::string, ConfigVariant> ConfigVariant;
Since making such a type definition would be impossible, is there any way around this?
Boost. Variant, part of collection of the Boost C++ Libraries. It is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types in a uniform manner.
The class template std::variant represents a type-safe union. An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value (this state is hard to achieve, see valueless_by_exception).
The three types of containers found in the STL are sequential, associative and unordered.
A container is an object that stores a collection of objects of a specific type. For example, if we need to store a list of names, we can use a vector . C++ STL provides different types of containers based on our requirements.
The official documentation has a section on recursive variant types. It explains two approaches: using boost::recursive_wrapper
and boost::make_recursive_variant
. I'm not sure that it is possible to define this kind of recursion with recursive_wrapper
(I have never been able to personally, but I'm far from an expert). In contrast with make_recursive_variant
it is really easy: you just need to replace your recursive variant type with boost::recursive_variant_
and then use ::type
to evaluate the metafunction and get the type you want.
typedef boost::make_recursive_variant<
double,
int,
std::string,
//std::map<std::string,ConfigVariant>
std::map<std::string,boost::recursive_variant_>
>::type ConfigVariant;
Running on coliru
#include <iostream>
#include <string>
#include <map>
#include <boost/variant.hpp>
typedef boost::make_recursive_variant<double, int, std::string, std::map<std::string, boost::recursive_variant_> >::type ConfigVariant;
struct printer : boost::static_visitor<>
{
void operator()(int val) const
{
std::cout << val;
}
void operator()(double val) const
{
std::cout << val;
}
void operator()(const std::string& val) const
{
std::cout << val;
}
void operator()(const std::map<std::string,ConfigVariant>& val) const
{
std::cout << "map_of{ ";
for(std::map<std::string,ConfigVariant>::const_iterator it=val.begin(),end=val.end(); it!=end; ++it)
{
boost::apply_visitor(*this,it->second);
std::cout << " ";
}
std::cout << "}";
}
};
int main()
{
ConfigVariant intconf=1;
ConfigVariant doubleconf=1.2;
ConfigVariant stringconf="conf";
std::map<std::string, ConfigVariant> mapconf, mapconf2;
mapconf["int"]=intconf;
mapconf["string"]=stringconf;
mapconf2["map"]=mapconf;
mapconf2["double2"]=doubleconf;
ConfigVariant visitable=mapconf2;
boost::apply_visitor(printer(), visitable);
std::cout << std::endl;
}
The question doesn't really have anything to do with boost::variant
; you are simply asking to make an n-ary tree using a standard container.
The answer is no, because the standard containers require complete types to be used as their template arguments. A container cannot contain itself because, as you observed, the definition would be recursive. Its constructor would presuppose that its constructor already existed. The result would be an incomplete type error.
As a special case, in fact std::vector
implementations often do allow this to be done. The constructor (and anything else requiring a complete element type) is not actually instantiated until the class definition of vector
is complete. And all the standard containers could be implemented to make it work in the same way. But it's not required by the Standard.
See also Can standard container templates be instantiated with incomplete types? ; this also contains a workaround. To make the workaround apply to variant
, which requires a complete type by itself, I'd suggest wrapping the incomplete type in std::unique_ptr
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With