What is the most compact way to extract class template arguments and iterate over them?

In the small program below, I will show the solution that I am currently using to extract class template arguments and iterate through a recursive helper function.

I wonder if there is a more concise way to do this, as I explain in the pseudo code in the comments below.

template <int...Is> struct Pack {}; template <int I> struct B { static void foo() { std::cout << I << "\n"; } }; // recursive helper function, also used to extract the parameter pack arguments template <int I, int...Is> void foo_helper( Pack<I, Is...>&& ) { B<I>::foo(); foo_helper( Pack<Is...>{} ); } // terminate recursion void foo_helper( Pack<>&& ) {} struct A { typedef Pack<1,3,5> ints; static void foo() { // this is what I do foo_helper(ints{}); // this is what I would like to do, ideally in one single line // 1) extract the template arguments pack from ints, without creating an helper function for that // 2) iterate on the template arguments of the pack without a recursive helper // In pseudocode, something like: // (B<IterateOver<ArgumentsOf<ints>>>::foo()); } }; int main() { A::foo(); } 
+6
source share
5 answers

This is the shortest I can think of:

 #include <iostream> template<int... Is> struct Pack; template <int I> struct B { static void foo() { std::cout << I << "\n"; } }; template<typename PACK> struct unpack; template<int...Is> struct unpack<Pack<Is...>> { template<template<int> class T> static void call() { using swallow = int[sizeof...(Is)]; (void) swallow{(T<Is>::foo(), 0)...}; } }; struct A { typedef Pack<1,3,5> ints; static void foo() { unpack<ints>::call<B>(); } }; int main() { A::foo(); } 
+1
source

If you want to do metaprogramming, start working with types. If you want non-piggy type template parameters, move them to types as soon as possible.

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 explicitly matches your ints package.

Then I introduce a tag type template. This is a type that β€œcarries” another type, but it does not have citizenship. You can extract the type from the value of the template instance as a bonus.

Thirdly, I write a function "for each type", which takes a lambda and a package of types, and proceeds to call a lambda once for each type, passing the type of tag.

In the body of a lambda, we can extract the passed type using decltype in the tag variable (or helper macro).

We combine them together, and from the resulting tag we can extract an integer in the source package.

As a result, you can enter this in 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 with ints "inline".

If we only decided to solve your specific problem, we would make a little less template, but I liked the pedigree.


 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 meta-programming in khana style: (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 allows you to iterate over a set 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 constexpr int i for each of the types in your list.

A letter from the above work raises your ints list into a list of types, because working with only types makes metaprogramming less special. You can skip this climb and write for_each_integer , which takes Pack<int...> directly with less code, but it seems less useful to me.

+3
source

You can add the foo_for_each function to the Pack :

 template <int...Is> struct Pack { template <template <int> class T> static void foo_for_each () { std::initializer_list<int> { (T<Is>::foo(),0)... } ; } }; 

Then you just write:

 ints::foo_for_each<B>(); 

This will call B<N>::foo for every N in the package.

As suggested by Jakk, you can pass a lambda that receives the tag type as an argument to create a generic Pack::for_each :

 template <typename T> struct tag { using type = T; }; template <typename T> using type_t = typename T::type; template <int...Is> struct Pack { template <template <int> class T, typename Func> static void for_each (Func&& func) { std::initializer_list<int> { ((std::forward<Func>(func)(tag<T<Is>>{})) 0)... } ; } }; 

Then you can call it like this:

 auto call_foo = [](auto tag) { type_t<decltype(tag)>::foo(); }; ints::for_each<B>(call_foo); 
+2
source

If you want to have a variational package for runtime iteration, you can attach std::array to your struct Pack as:

 template <int...Is> struct Pack { std::array<int, sizeof...(Is)> arr = {{Is...}}; }; 

And then iterate over as:

 static void foo() { for(auto && i : ints{}.arr) std::cout << i << " "; } 

Live demo

+1
source

What you wrote here is just weird, where did you even find the implementation of this hard?

  • I need an auxiliary function, this is just a fact, you could probably get around this anyway, but I don't see the point.

    The only solution for this right now is to use Clang 3.6, they have already implemented a new syntax that allows you to write something like this.

    // I'm sure it was syntax, it was called fold expression // you can learn more about it here:
    // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4295.html

     template<typename ... Type> auto sum(Type ... argument) { return (... + argument); } 

    In any other compiler, how to do this, you need to write two simple functions

     template<typename Tail> auto sum(Tail tail) { return tail; } template<typename Head, typename ... Tail> auto sum(Head head, Tail ... tail) { return head + sum(tail); } 

    This accepts anything that supports +, so strings, ints, double will work, maybe a little more, but you get the gist of it.

    Your example will look as follows

     template<typename Tail> void print(Tail tail) { cout << tail << endl; } template<typename Head, typename ... Tail> void print(Head head, Tail ... tail) { cout << head; print(tail...); } 

    Using:

     print(1, 3.14, "something", string{"yeye"}, 52); 

    or

     sum(1, 512, 55, 91); 

    There are other ways to use variable templates, as this guy describes here, there is too much of this for me to put it here, so I’ll just contact you:

    http://florianjw.de/en/variadic_templates.html

  • Iterating over template arguments is a bit more complicated because you have to use some real compiler magic and index_sequence.

    I have an example lying somewhere here because I have been messing with it lately.

     template<typename InputTuple, std::size_t ... N> void tupleIteratorImpl(InputTuple& input, std::index_sequence<N...>) { // DO WHATEVER YOU WANT HERE, but the structure is FUNCTION(/* pass all of the template parameters as arguments */, std::get<N>(input)...); // and FUNCTION has to have the structure of the examples from point 1. // but with this, you can already do pretty much anything you imagine // even at compile time } template<typename InputTuple, typename Indices = std::make_index_sequence<std::tuple_size<InputTuple>::value>> void tupleIterator(InputTuple& input) { tupleIteratorImpl(input, Indices()); } 

    The function for this is already included in C ++ 17, and it is called apply, here is the documentation: http://en.cppreference.com/w/cpp/experimental/apply with some sample code.

Hope this answers some of your questions.

+1
source

All Articles