6

Giả sử rằng tôi có một số boost::variant -type TNested chứa một số loại và một số loại khác boost::variant (chính nó không thể chứa lại boost::variant types)).Tạo loại tăng mới biến thể từ loại tăng cường lồng nhau được đưa ra

Tôi đang tìm một bí danh mẫu flatten<TNested> sẽ đánh giá loại boost::variant không có lồng nhau boost::variant s, ví dụ: TFlatten, trong khi các loại trùng lặp có thể đang bị xóa, ví dụ: int chỉ xảy ra một lần.

Tôi thực sự không có ý tưởng, nếu điều này có thể được thực hiện bằng cách nào đó.

#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

bạn có thể sử dụng C++ 17/14/11? –

+0

Tôi có thể sử dụng ít nhất C++ 11. – Aleph0

+0

@FrankSimon vui lòng xem câu trả lời đã chỉnh sửa –

Trả lời

4

wandbox example

Dưới đây là một C++ 11 đệ quy giải pháp dựa trên mẫu có giá trị làm việc với ví dụ của bạn:

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

ý tưởng chung:

std::tuple được sử dụng làm danh sách loại chung và std::tuple_cat được sử dụng để nối các danh sách loại.

Một flatten_variant<TResult, Ts...> metafunction đệ quy được xác định mà thực hiện như sau:

  • TResult sẽ được lấp đầy với các loại phẳng và sẽ được trả lại vào cuối của đệ quy.

    • Việc đệ quy kết thúc khi Ts... trống.
  • Các loại không biến thể được thêm vào TResult.

  • Các loại biến thể được giải nén và tất cả các loại bên trong của chúng được làm phẳng đệ quy, sau đó được thêm vào TResult.


Thực hiện:

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

Cảm ơn bạn đã trả lời nhanh. Tôi sẽ mất vài ngày để tiêu hóa nó. :-) – Aleph0

2

Vấn đề với boost::variant là nó không phải là mẫu thực variadic chỉ mô phỏng một bằng cách thêm một số thông số mẫu với giá trị mặc định boost::detail::variant::void_. Để làm việc với nó như với mẫu variadic, bạn cần phải thay đổi nó trước tiên thành gói có các loại có liên quan. Toàn bộ C++ 11 sẵn sàng sử dụng cách tiếp cận để làm những gì bạn muốn có thể trông như sau. Cách tiếp cận này tính đến cả độ phẳng cũng như giả định duy nhất đối với loại kết quả. Phần integer_sequence sẽ không cần thiết nếu bạn quyết định sử dụng 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"); 
} 
Các vấn đề liên quan