c++17variadic-templatestemplate-meta-programmingtype-definitionindex-sequence

Recursive aggregate type configured using many template parameters using std::index_sequence


There is a class template:

template<std::size_t ID, std::size_t T1, std::size_t T2, std::size_t T3>
class Feature { /* Implementation goes here */ };

All the instantiations of Feature<...> are 'collected' here:

template<typename FEATURE, typename... OTHERS>
class Features<FEATURE, OTHERS...> : public Features<OTHERS...> {
public:
    /* Operations defined here */
private:
    FEATURE m_feature;
};

All the features are created as follows:

using FeatureConfig = Features<Feature<0, 1, 2, 3>, Feature<1, 4, 5, 6>>;
FeatureConfig m_features;

So far so good. My task is to get rid of those hard coded values in there 1..3, 4..6 etc. The way to do so is to have generated header file which contains the configuration for all the features. Something like:

template<std::size_t> struct Config;

template<>
struct Config<0> {
    static constexpr std::size_t t1 { 1 };
    static constexpr std::size_t t2 { 2 };
    static constexpr std::size_t t3 { 3 };
};

template<>
struct Config<1> {
    static constexpr std::size_t t1 { 4 };
    static constexpr std::size_t t2 { 5 };
    static constexpr std::size_t t3 { 6 };
};

Then I need to change type definition of FeatureConfig somehow to use the specializations of FeatureConfig based on an index (0, 1, ...). My unsuccessfull try is:

template<std::size_t... INDEX_SEQUENCE>
using FeatureConfig = Features<Feature<INDEX_SEQUENCE, Config<INDEX_SEQUENCE>::t1, Config<INDEX_SEQUENCE>::t2, Config<INDEX_SEQUENCE>::t3>...>;

FeatureConfig<std::make_index_sequence<2>> m_features;

It seems I am somehow mixing type and value...

Many thanks in advance to anyone willing to help me fix the incorrect code in my last listing up there.

Cheers Martin


Solution

  • If I understand correctly what do you want...

    I propose the declaration (no definition required because is used only inside a decltype()) of the following function

    template <std::size_t ... Is>
    auto getFeaturesType (std::index_sequence<Is...>)
       -> Features<Feature<Is, Config<Is>::t1, Config<Is>::t2, Config<Is>::t3>...>;
    

    Now you can define FeatureConfig simply as follows

    template <std::size_t N>
    using FeatureConfig
       = decltype(getFeaturesType(std::make_index_sequence<N>{})); 
    

    The following is a full compiling (simplified) example

    #include <type_traits>
    #include <utility>
    
    template <std::size_t, std::size_t, std::size_t, std::size_t>
    struct Feature { };
    
    template <typename...>
    struct Features
     { };
    
    template <typename F, typename... Os>
    struct Features<F, Os...> : public Features<Os...>
     { F m_feature; };
    
    template <std::size_t N>
    struct Config
     {
       static constexpr std::size_t t1 { N*3u };
       static constexpr std::size_t t2 { 1u + N*3u };
       static constexpr std::size_t t3 { 2u + N*3u };
     };
    
    template <std::size_t ... Is>
    auto getFeaturesType (std::index_sequence<Is...>)
       -> Features<Feature<Is, Config<Is>::t1, Config<Is>::t2, Config<Is>::t3>...>;
    
    template <std::size_t N>
    using FeatureConfig
       = decltype(getFeaturesType(std::make_index_sequence<N>{}));
    
    int main () 
     {
       using T1 = FeatureConfig<2u>;
       using T2 = Features<Feature<0u, 0u, 1u, 2u>, Feature<1u, 3u, 4u, 5u>>;
    
       static_assert( std::is_same<T1, T2>::value, "!" );
     }
    

    If I understand correctly how do you use Config (if t1 is ever N*3u, if if t2 is ever 1u+N*3u and if t3 is ever 2u+N*3u), you can avoid Config at all and write getFeaturesType as follows

    template <std::size_t ... Is>
    auto getFeaturesType (std::index_sequence<Is...>)
       -> Features<Feature<Is, Is*3u, Is*3u+1u, Is*3u+2u>...>;