If you want to do metaprogramming, start working in types. If you want non-type template parameters, move them over to types asap.
Below, I first take Pack<1,2,3>
and convert it to types< std::integral_constant<int, 1>, std::integral_constant<int, 2>, std::integral_constant<int, 3> >
. This is a list of types that is in obvious correspondence to your pack of ints.
Then, I introduce a tag type template. This is a type which "carries" another type, but it itself is stateless. You can extract the type from an value of an instance of the template as a bonus.
Third, I write a "for each type" function that takes a lambda and a pack of types, and proceeds to call the lambda once for each of the types, passing in a tag type.
In the body of the lambda, we can extract the passed type by using decltype
on the tag variable (or a helper macro).
We chain those together, and from the passed tag type we can extract the integer in the original pack.
The result is you can inject this into your code:
for_each_type( [&](auto tag){
constexpr int i = TAG_TYPE(tag){};
// use i
}, ints_as_types_t<ints>{} );
in the middle of your method, and work on the ints "inline".
If we wanted to only solve your specific problem, we'd do a bit less boilerplate, but I like the genericness.
template<class...>struct types{using type=types;};
template <int...Is> struct Pack {};
template<class pack> struct ints_as_types;
template<class pack>
using ints_as_types_t=typename ints_as_types<pack>::type;
template<class T, template<T...>class pack, T...ts>
struct ints_as_types<pack<ts...>> {
using type=types<std::integral_constant<T,ts>...>;
};
now we can do:
using pack = ints_as_types_t<Pack<1,2,3>>;
and pack
is a list of types, not a list of integers.
Now some hana-style metaprogramming: (metaprogramming with values instead of pure types)
template<class T>struct tag_t{using type=T; constexpr tag_t(){};};
template<class T>constexpr tag_t<T> tag={};
template<class Tag>using type_t=typename Tag::type;
#define TAG_TYPE(...) type_t<std::decay_t<decltype(__VA_ARGS__)>>;
template<class F, class...Ts>
void for_each_type(F&& f, types<Ts...>) {
using discard=int[];
(void)discard{ 0, ((
f(tag<Ts>)
),void(),0)...};
}
which lets you iterate over a collection of types.
for_each_type( [&](auto tag){
constexpr int i = TAG_TYPE(tag){};
// use i
}, ints_as_types_t<ints>{} );
gives you a lambda that has a constexpr int i
for each of the types in your list.
A bunch of the above work lifts your list of ints into a list of types, because working with only types makes metaprogramming less special-case. You can skip that lifting, and write a for_each_integer
that takes a Pack<int...>
directly with less code, but it seems less useful to me.