I'm trying to create a wrapper for variant that allows me to wrap a bunch of types passed in another type, and put them into the variant. How would I expand a tuple's types and place them into the variant conditionally based on whether the type is a container?
template <typename T>
struct Wrapper
{};
template <typename>
struct has_expandable_types : std::false_type
{
};
template <template <typename...> class T, typename... Args>
struct has_expandable_types<T<Args...>> : std::true_type
{
};
template <template <typename> class T, typename... Types>
struct Variant_wrapper;
template <template <typename> class T, typename U, typename... Types>
struct Variant_wrapper_expanded;
template <template <typename> class T, typename... Types>
struct Variant_wrapper : std::variant<T<Types>...>
{
};
template <template <typename> class T, template <typename...> class U, typename... UTypes>
struct Variant_wrapper_expanded <T, U<UTypes...>> : Variant_wrapper<T, UTypes...>
{
};
using Types = std::tuple<int, float>;
int main()
{
//std::variant< Wrapper<int>, Wrapper<float> >, OK
Variant_wrapper<Wrapper, int, float> var;
//std::variant< Wrapper<int>, Wrapper<float> >, OK
Variant_wrapper_expanded<Wrapper, Types> var2;
//std::variant< Wrapper<int>, Wrapper<float> >, NOT OK
// EXPECTED std::variant< Wrapper<int>, Wrapper<float>, Wrapper<double> >
//can't capture a second parameter pack?
Variant_wrapper_expanded<Wrapper, Types, double> var3;
}
The ideal usage would be something like
//produces std::variant< Wrapper<std::string>, Wrapper<int>, Wrapper<float>, Wrapper<double> >
Variant_wrapper<Wrapper, std::string, Types, double> var;
Variant_wrapper
wraps the entire tuple but I'd like to be able to expand the held types and insert them into the variant instead. How would I do the expansion?With C++17 CTAD, you might do:
// Tuple_expander<A, std::tuple<B, C>, D>::type -> std::tuple<A, B, C, D>
template <typename... Ts>
struct Tuple_expander
{
using type = decltype(std::tuple_cat(std::tuple{std::declval<Ts>()}...));
// trick is here:
// std::tuple{std::tuple{..}} use copy constructor
// std::tuple{..} wrap the type in a tuple.
};
// The mapping/transformation of the (flat) tuple
template <template <typename> class C, typename T> struct TupleToVariantWrapper;
template <template <typename> class C, typename... Ts>
struct TupleToVariantWrapper<C, std::tuple<Ts...>>
{
using type = std::variant<C<Ts>...>;
};
// No real changes.
template <template <typename> class C, typename... Ts>
struct Variant_wrapper
{
using type = std::variant<C<Ts>...>;
};
template <template <typename> class C, typename... Ts>
struct Variant_wrapper_expanded
{
using type = typename TupleToVariantWrapper<C, typename Tuple_expander<Ts...>::type>::type;
};
(I use ::type
instead of inheritance)