Creating a new type of forced variant from the given nested variant of the forward variant

Suppose I have a nested boost::variant -type TNested containing some types and some other types of boost::variant (which alone cannot contain boost::variant types again, so there will be no recursion).

I am looking for an alias for the flatten<TNested> template that will evaluate the type boost::variant , has no boost::variant s, for example. TFlatten , while possible duplicate types are removed, for example. int only happens once.

I have no idea if this can be achieved in any way.

 #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; } 
+6
source share
2 answers

wandbox example

Here's a valid recursive solution based on C ++ 11 templates 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 used as a generic type list, and std::tuple_cat used to combine type lists.

A flatten_variant<TResult, Ts...> defines a recursive metafunction that does the following:

  • TResult will be populated with oblate types and will be returned at the end of the recursion.

    • The recursion ends when Ts... empty.
  • Non-invariant types are added to TResult .

  • Variant types are unpacked, and all their internal types are recursively flattened, and then added 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; 
+4
source

The problem with boost::variant is that it is not a real variation template, which only simulates by adding several template parameters with the default value boost::detail::variant::void_ . To work with it as a variation template, you must first transfer it to a package with the corresponding types. A complete C ++ 11 ready-to-use approach to do what you want might look like this. The approach takes into account both the plane and the assumption of uniqueness to the type of result. The integer_sequence part integer_sequence be superfluous 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"); } 
+2
source

All Articles