c++templatesvariadic-templatesspecializationstdtuple

How do I obtain the variadic arguments from an std::tuple to unpack for another std::tuple?


I am trying to convert a list of template arguments (enum class, not typenames) to corresponding typenames for the template arguments to an std::tuple. I'm guessing if I can get somehow name the variadic list as a using type = ..., I might be able to expand that in the next template recursion

So, I have the enum class:

enum class object_t{
    STR,
    INT,
    FLOAT,
};

and the template supposed to provide the concrete class (some std::tuple):

template <object_t _obj, object_t ...Args>
struct concrete {
        // here's the part I need to correct
    using type = std::tuple<typename concrete<_obj>::type, typename concrete<Args...>::type>;
};

the specializations to tail off the recursions:

template <>
struct concrete<object_t::STR> {
    using type = std::string;
};

template <>
struct concrete<object_t::INT> {
    using type = int64_t;
};

template <>
struct concrete<object_t::FLOAT> {
    using type = double;
};

and the using declaration for the ::type shorthand

template<object_t _obj, object_t ...Args>
using concrete_t = typename concrete<_obj, Args...>::type;

Eventually, I want something like

concrete_t<object_t::INT, object_t::FLOAT, object_t::STR>

to be equivalent to

std::tuple<int64_t, double, std::string>

Currently, that should produce something like:

std::tuple<int64_t, std::tuple<double, std::string>>

instead.

I'm not the best on variadic templates, but i'm thinking if the using type (of the general template) was a parameter pack instead of a tuple, I might be able to unpack it for the next tuple (whose parameter list I will then have to obtain again and so on). Something like:

template <object_t _obj, object_t ...Args>
struct concrete {
    using type = std::tuple<typename concrete<_obj>::type, typename concrete<Args...>::type...>::elements_type;
};

where the elements_type is a variadic pack and the ::type... unpacks it

But even that doesn't seem right, since the root ::type will be a parameter pack and not an std::tuple like desired. Maybe another template is needed, I do not know.

Anything advice might go a long way, thanks!


Solution

  • In case someone needs this, the solution, thanks to @IgorTandetnik, looks like this:

    enum class object_t{
        STR,
        INT,
        FLOAT,
    };
    
    template<object_t _obj>
    struct concrete_traits;
    
    template<>
    struct concrete_traits<object_t::STR> {
        using type = std::string;
    };
    
    template<>
    struct concrete_traits<object_t::INT> {
        using type = int64_t;
    };
    
    template<>
    struct concrete_traits<object_t::FLOAT> {
        using type = double;
    };
    
    template <object_t ...Args> struct concrete {
        using type = std::tuple<typename concrete_traits<Args>::type...>;
    };
    
    template<object_t ...Args>
    using concrete_t = typename concrete<Args...>::type;
    
    

    This answer is based on @IgorTandetnik comment.

    To eliminate the std::tuple in the trivial case of a single template argument, the specialization

    template<object_t _obj>
    struct concrete<_obj> {
        using type = typename concrete_traits<_obj>::type;
    };
    

    makes, for example, concrete_t<object_t::STR> to be std::string instead of std::tuple<std::string>.