c++templatesc++17variadic-templatestype-traits

Create a variant from an array whose elements are transformed into types


I have run into a strange challenge trying to define a type based on an array while writing some library binding code.

Consider an enumeration like the following:

enum class MyTypes
{
    UInt,
    Int,
    Float
}

With some type traits, I can convert a value of the enumeration into a type:

template <MyTypes Ty> struct typeify;
template <> struct typeify<MyTypes::UInt> { using type = unsigned int; };
template <> struct typeify<MyTypes::Int> { using type = int; };
template <> struct typeify<MyTypes::Float> { using type = float; };
template <MyTypes Ty> using typeify_t = typename typeify<Ty>::type;

To help with the binding, I also have this (because C++ doesn't have reflection):

inline static constexpr std::array KnownTypes 
{ 
    std::pair{"UInt", MyTypes::UInt},
    std::pair{"Int", MyTypes::Int},
    std::pair{"Float", MyTypes::Float}
};

From here, I want to define a std::variant based on my known types.

With 3 values, the obvious thing is to just write the variant like so:

std::variant<unsigned int, int, float>

But, in my situation, I have 50+ types. So, I would like a way to automatically generate the type definition for the std::variant from the array of KnownTypes and the typeify_t type trait.

I think I have gotten close with the following, but I have been stymied by not being able to give a variadic template parameter a default value:

template <template <typename T, T ... Idx> class temp = std::make_index_sequence<KnownTypes.size()>>
using AnyKnownType = std::variant<typeify_t<KnownTypes[Idx].second>, ...>;

Solution

  • Any problem in programming can be solved with another level of indirection (except too many levels of indirection).

    Instead of directly creating a using expression template, create a function template declaration and use its return type in your using expression:

    template <std::size_t... Is>
    std::variant<typeify_t<KnownTypes[Is].second>...>
    make_variant_type(std::index_sequence<Is...>);
    
    using AnyKnownType = decltype(make_variant_type(std::make_index_sequence<KnownTypes.size()>()));
    

    Demo