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
) ?
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{});
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>();
});
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>()
).