The problem with boost::variant
is that it isn't the real variadic template only simulating one by adding a number of template parameters with default value boost::detail::variant::void_
. To work with it as with variadic template, one need to change it first to the pack with relevant types. The full c++11 ready to use approach to do what you want could look as follows. The approach takes into account both flatness as well as uniqueness assumption to the result type. The integer_sequence
part will be unnecessary if you decide to use c++14:
#include <boost/variant.hpp>
#include <tuple>
#include <type_traits>
template <class T, T... Vs>
struct integer_sequence { };
template <class T, class, class, class = integer_sequence<T>, class = integer_sequence<T, 0>, class = void>
struct make_integer_sequence_impl;
template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 0>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, typename std::enable_if<(ICV1 > 0)>::type>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Res...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };
template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 1>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, void>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Pow..., (Res + sizeof...(Pow))...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };
template <class T, class Res, class Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, 0>, std::integral_constant<T, 0>, Res, Pow, void> {
using type = Res;
};
template <class T, T V>
using make_integer_sequence = typename make_integer_sequence_impl<T, std::integral_constant<T, V/2>, std::integral_constant<T, V%2>>::type;
template <class>
struct is_variant_void {
static constexpr size_t value = 0;
};
template <>
struct is_variant_void<boost::detail::variant::void_> {
static constexpr size_t value = 1;
};
constexpr size_t sum(size_t v){
return v;
}
template <class... Args>
constexpr size_t sum(size_t v, Args... vs){
return v + sum(vs...);
}
template <class Variant>
struct variant_size;
template <class... Ts>
struct variant_size<boost::variant<Ts...>> {
static constexpr size_t value = sizeof...(Ts) - sum(is_variant_void<Ts>::value...);
};
template <class...>
struct pack { };
template <class V, class=make_integer_sequence<size_t, variant_size<V>::value>>
struct variant_to_pack;
template <class... Ts, size_t... Is>
struct variant_to_pack<boost::variant<Ts...>, integer_sequence<size_t, Is...>> {
using type = pack<typename std::tuple_element<Is, std::tuple<Ts...>>::type...>;
};
template <class>
struct unique_opaque { };
template <class... Ts>
struct unique_pack: unique_opaque<Ts>... { };
template <class, class, class = void>
struct unique;
template <class... Res, class T, class... Ts>
struct unique<unique_pack<Res...>, pack<T, Ts...>, typename std::enable_if<std::is_base_of<unique_opaque<T>, unique_pack<Res...>>::value>::type>:
unique<unique_pack<Res...>, pack<Ts...>> { };
template <class... Res, class T, class... Ts>
struct unique<unique_pack<Res...>, pack<T, Ts...>, typename std::enable_if<!std::is_base_of<unique_opaque<T>, unique_pack<Res...>>::value>::type>:
unique<unique_pack<Res..., T>, pack<Ts...>> { };
template <class... Res>
struct unique<unique_pack<Res...>, pack<>, void> {
using type = boost::variant<Res...>;
};
template <class...>
struct flatten;
template <class... Res, class T, class... Rest>
struct flatten<pack<Res...>, T, Rest...>: flatten<pack<Res..., T>, Rest...> { };
template <class... Res, class... Vs, class... Rest>
struct flatten<pack<Res...>, boost::variant<Vs...>, Rest...>: flatten<pack<Res...>, typename variant_to_pack<boost::variant<Vs...>>::type, Rest...> {};
template <class... Res, class... Vs, class... Rest>
struct flatten<pack<Res...>, pack<Vs...>, Rest...>: flatten<pack<Res...>, Vs..., Rest...> { };
template <class Res>
struct flatten<Res>:unique<unique_pack<>, Res> { };
int main() {
boost::variant<int, boost::variant<float, int, boost::variant<char, bool>, bool>> vif;
static_assert(std::is_same<flatten<pack<>, decltype(vif)>::type, boost::variant<int, float, char, bool>>::value, "Test");
}