Assume that I have a nested boost::variant
-type TNested
containing some types and some other boost::variant
types (that itself cannot contain again boost::variant types
, so that there will be no recursion).
I'm looking for a template alias flatten<TNested>
that would evaluate to a boost::variant
type have no nested boost::variant
s, e.g. TFlatten
, whereas possible duplicate types are being removed, e.g. int
occurs only once.
I have really no idea, if this can be accomplished somehow.
#include <boost/variant.hpp>
#include <boost/any.hpp>
#include <iostream>
struct Person;
typedef boost::variant<int, double, boost::variant<std::string, int>, boost::variant<Person>> TNested;
typedef boost::variant<int, double, std::string, Person> TFlatten;
template<typename NestedVariant>
using flatten = //???
int main() {
flatten<TNested> x;
std::cout << typeid(x) == typeid(TFlatten) << std::endl;
}
Here's a valid C++11 recursive template-based solution that works with your example:
using nested = variant<int, double, variant<std::string, int>, variant<Person>>;
using flattened = variant<int, double, std::string, int, Person>;
static_assert(std::is_same<flatten_variant_t<nested>, flattened>{}, "");
General idea:
std::tuple
is used as a generic type list and std::tuple_cat
is used to concatenate type lists.
A flatten_variant<TResult, Ts...>
recursive metafunction is defined which does the following:
TResult
will get filled with flattened types and will be returned at the end of the recursion.
Ts...
is empty.Non-variant types are appended to TResult
.
Variant-types are unpacked, and all their inner types are recursively flattened, then appended to TResult
.
Implementation:
namespace impl
{
// Type of the concatenation of all 'Ts...' tuples.
template <typename... Ts>
using cat = decltype(std::tuple_cat(std::declval<Ts>()...));
template <typename TResult, typename... Ts>
struct flatten_variant;
// Base case: no more types to process.
template <typename TResult>
struct flatten_variant<TResult>
{
using type = TResult;
};
// Case: T is not a variant.
// Return concatenation of previously processed types,
// T, and the flattened remaining types.
template <typename TResult, typename T, typename... TOther>
struct flatten_variant<TResult, T, TOther...>
{
using type = cat<TResult, std::tuple<T>,
typename flatten_variant<TResult, TOther...>::type>;
};
// Case: T is a variant.
// Return concatenation of previously processed types,
// the types inside the variant, and the flattened remaining types.
// The types inside the variant are recursively flattened in a new
// flatten_variant instantiation.
template <typename TResult, typename... Ts, typename... TOther>
struct flatten_variant<TResult, variant<Ts...>, TOther...>
{
using type =
cat<TResult, typename flatten_variant<std::tuple<>, Ts...>::type,
typename flatten_variant<TResult, TOther...>::type>;
};
template<typename T>
struct to_variant;
template<typename... Ts>
struct to_variant<std::tuple<Ts...>>
{
using type = variant<Ts...>;
};
}
template <typename T>
using flatten_variant_t =
typename impl::to_variant<
typename impl::flatten_variant<std::tuple<>, T>::type
>::type;