c++templatestemplate-classestypelist

Iterating over a list of template classes


I have a Runner template class defined as follows (note that its API shouldn't be modified)

template<typename T>
struct Runner  {
    template<template<typename> typename U>
    auto dosomething() {
        U<T> u; // instantiation of the incoming template class U with the template argument T
    }
};

I can instantiate Runner and then call dosomething with various template classes

template<typename T> struct A {};
template<typename T> struct B {};
template<typename T> struct C {};

int main()
{
    Runner<int> runner;
    runner.dosomething<A>();
    runner.dosomething<B>();
    runner.dosomething<C>();
}

I would like to have some types list for A, B and C, and then iterate X over this list in order to call runner.dosomething<X>();, e.g.

// PSEUDO-CODE (obviously non-sense in c++)
using typelist = magictypelist<A,B,C>;
typelist::iterate ([&] <typename X> { 
    runner.dosomething<X>();
});

Using std::tuple easily comes to mind with the possibility to iterate each type. Unfortunately it can't be used in that context since A, B and C are template classes and not actual classes.

The closest thing I can think of is a template alias such as

template<typename T>
using typeslist = std::tuple<A<T>,B<T>,C<T>>;

but I can't see any way to iterate each type (with std::tuple_element_t) and "remove" the T template and then call dosomething

Question: is there a way to iterate over a list of template classes or is it impossible (without modifying Runner) ?


Solution

  • You might create a tuple-like type for template template parameter:

    template <template<typename> typename... Us>
    struct template_type_list{};
    
    template<typename T> struct A {};
    template<typename T> struct B {};
    template<typename T> struct C {};
    using typelist = template_type_list<A,B,C>;
    

    Then, directly

    Runner<int> runner;
    
    [&]<template<typename> class... Cs>(template_type_list<Cs...>) {
        (runner.dosomething<Cs>(), ...);
    }(typelist{});
    

    Demo

    Or add some helper:

    template <template<typename> typename... Us, typename F>
    void iterate(template_type_list<Us...>, F f)
    {
        (f(template_type_list<Us>{}), ...);
        // (f.template operator()<Us>(), ...); // Demo2
    }
    

    and

    Runner<int> runner;
    iterate(typelist{}, [&]<template<typename> class C>(template_type_list<C>) {
    // iterate(typelist{}, [&]<template<typename> class C>(){ // Demo2
        runner.dosomething<C>();
    });
    

    Demo1 / Demo2

    Note: I prefer to pass deducible type in lambda (Demo1), requiring [&]<template<typename> class C>(template_type_list<C>) instead of [&]<template<typename> class C>() (Demo2). That allows, IMO, more natural call (f(template_type_list<Us>{}) instead of f.template operator()<Us>()).