6

Załóżmy, że mam zagnieżdżony boost::variant -type TNested zawierające niektóre rodzaje i kilka innych boost::variant typów (który sam w sobie nie może zawierać ponownie boost::variant types, tak że nie będzie rekurencji).Tworzenie nowego typu doładowania wariantowej od rodzaju podano zagnieżdżona doładowania wariantem

Szukam szablonu aliasu flatten<TNested>, który byłby oceniany na typ boost::variant, nie ma zagnieżdżonych boost::variant s, np. TFlatten, podczas gdy możliwe zduplikowane typy są usuwane, np. int występuje tylko raz.

Nie mam pojęcia, jeśli uda się to jakoś zrealizować.

#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; 
} 
+0

można używać C++ 17/14/11? –

+0

Mogę użyć co najmniej C++ 11. – Aleph0

+0

@FrankSimon zobacz edytowaną odpowiedź –

Odpowiedz

4

wandbox example

Oto ważny C++ 11 rekurencyjne Rozwiązanie oparte na szablonach, które współpracuje z przykładu:

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>{}, ""); 

Ogólny pomysł:

std::tuple jest używana jako ogólna lista typów, a std::tuple_cat służy do łączenia list typów.

flatten_variant<TResult, Ts...> rekurencyjne metafunkcji definiuje który wykonuje następujące czynności:

  • TResult dostanie wypełniony spłaszczonymi typów i zostanie zwrócona na koniec rekursji.

    • Rekursja kończy się, gdy Ts... jest pusta.
  • Nie-wariantowe typy są dołączane do TResult.

  • Typy wariantów są rozpakowywane, a wszystkie ich wewnętrzne typy są rekursywnie spłaszczane, a następnie dołączane do TResult.


Realizacja:

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; 
+0

Dzięki za szybką odpowiedź. Przeczytanie go zajmie mi kilka dni. :-) – Aleph0

2

Problem z boost::variant jest to, że nie jest prawdziwe o zmiennej liczbie argumentów szablonu tylko jedną symulację dodając szereg parametrów szablonu z domyślnej wartości boost::detail::variant::void_. Aby pracować z nim tak jak przy szablonie variadic, trzeba go najpierw zmienić na pakiet z odpowiednimi typami. Pełna gotowość do użycia C++ 11 do robienia tego, co chcesz, może wyglądać następująco. Podejście uwzględnia zarówno płaskość, jak i założenie unikalności dla typu wyniku. integer_sequence część będzie konieczne, jeśli użytkownik zdecyduje się użyć 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"); 
}