c++templatesvariadic-templates

Function call from multiple variadic template lists


(maybe the title already falls in XY problem...)

Given this templated function:

template <typename T, typename U, typename V, typename W>
void MyFunc()
{
    // stuff...
}

I would like to call it for all given combinations of types. For example, I would like to call for:

In pseudo code, it would look like:

foreach (t in { char, int, long long })
{
    foreach (u in { float, double })
    {
        foreach (v in { std::uint64_t, std::string_view })
        {
            foreach (w in { int, MyEnum1, MyEnum2, MyEnum3 })
            {
                MyFunc<t, u, v, w>();
            }
        }
    }
}

How can I achieve that?

I feel variadic templates tricks/patterns like tuples or "type lists" are the way to go, but I failed at sorting it out.


Solution

  • I do not know if this is the most elegant way, but at least it is working:

    #include <cstdint>
    #include <iostream>
    
    template<typename U, typename V, typename W>
    void MyFunc() {
        std::cout << typeid(U).name() << ", " << typeid(V).name() << ", " << typeid(W).name() << std::endl;
    }
    
    template<typename... Types>
    struct TypeContainer {
    };
    
    template<typename U, typename V, typename Ws>
    struct for_loops_1;
    
    template<typename U, typename V, typename... Ws>
    struct for_loops_1<U, V, TypeContainer<Ws...> > {
        static void call() {
            (MyFunc<U, V, Ws>(), ...);
        }
    };
    
    template<typename U, typename Vs, typename Ws>
    struct for_loops_2;
    
    template<typename U, typename... Vs, typename Ws>
    struct for_loops_2<U, TypeContainer<Vs...>, Ws> {
        static void call() {
            (for_loops_1<U, Vs, Ws>::call(), ...);
        }
    };
    
    template<typename Us, typename Vs, typename Ws>
    struct for_loops_3;
    
    template<typename... Us, typename V, typename W>
    struct for_loops_3<TypeContainer<Us...>, V, W> {
        static void call() {
            (for_loops_2<Us, V, W>::call(), ...);
        }
    };
    
    int main() {
        for_loops_3<
            TypeContainer<float, double>,
            TypeContainer<uint64_t, std::string_view>,
            TypeContainer<int, short, long>
        >::call();
    }
    

    My version has only 3 variadic argument lists, but it should be easy to extend to idea to any fixed number of type lists.

    The core idea is to get multiple variadic argument list by capturing them into a struct that itself has variadic template arguments. This allows to pass in multiple lists into one struct/function.

    And then I have used structs instead of functions to make use of partial specialization, which allow you to access the variadic arguments that are packed into the TypeContainer.