Tuesday, November 13, 2012

c++11 Variadic template expansion options

While hacking up this movable bind function I got distracted(as per usual) thinking about the range of possible variadic template expansions available to us.

I figure that there are 2 main choices to make when using the argument packed types.
  1. Argument expansion or Argument Recursion
  2. Direct or Indirect

Argument expansion is where you flatten out the argument with the .... and pass them on.
  • The direct form of this is to work the expansion on the Argument is self.
  • The indirect for is to use a helper function around the Argument and expand the function passing it each object.

Argument recursion is where you siphon off one or more arguments on each pass and call down into your templates for the next stage.

  • The direct form is to do this in your main struct or function
  • The indirect form of this is where you create an inner helper or core that handles the recursive part for you.

Heres a triplet of examples that show the main uses cases that I would expect to see.. I guess there might be more but nothing else comes to mind at the moment... 
#include <iostream>
#include <utility>

// Indirect recursive class expansion
template <typename Func, typename A, typename ...Args> 
struct CallForEachHelper
{
  static void call(Func & f, A && a, Args && ...args)
  {
    f(std::forward<A>(a));
    CallForEachHelper<Func, Args...>::call(f, args...);
  }
};

template <typename Func, typename A> 
struct CallForEachHelper<Func, A>
{
  static void call(Func & f, A && a)
  {
    f(std::forward<A>(a));
  }
};

template <typename Func, typename ...Args>
void CallForEachIndirect(Func & f, Args && ...args)
{
  CallForEachHelper<Func, Args...>::call(f, std::forward<Args>(args)...);
}

// Direct recursive function expansion
template <typename Func, typename Arg>
void CallForEachDirect(Func & f, Arg arg)
{
  f(std::forward<Arg>(arg));
}

template <typename Func, typename Arg, typename ...Args>
void CallForEachDirect(Func & f, Arg arg, Args && ...args)
{
  f(std::forward<Arg>(arg));
  CallForEachDirect<Func, Args...>(f, std::forward<Args>(args)...);
}

// direct non-recursive argument flattening
template <typename Func, typename ...Args>
void CallForAll(Func & f, Args && ...args)
{
  f(std::forward<Args>(args)...); //indirect argument flattening using std::forward
}

void foo(int a)
{
  std::cout << "a:" << a << "\n";
}

void foo3(int a, int b, int c)
{
  std::cout << "a:" << a 
     << " b:" << b
     << " c:" << c 
     << "\n";
}

int main()
{
  int a1=1;
  int a2=2;
  int a3=3;
    
  CallForEachIndirect(foo, a1, a2, a3);  // run foo on each item via helper class recursion
  CallForEachDirect(foo, a1, a2, a3);    // run foo on each item by direct recursion
  CallForAll(foo3, a1, a2, a3);          // run foo3 on all items in 1 shot.
}

No comments:

Post a Comment