C/C++ Preprocessor Metaprogramming - Applications: Dynamic Template Parameters

So with all these features, lets actually look at some examples. The first example I want to go into is dynamic template parameters (for a limited range of parameters).

Motivation

Lets say you have a template that has some template parameter N that you wish to manipulate (with a defined values) at run time. An example of this is something like this is

template<int N> SomeReturnType Foo(SomeParams)
{
  // Do something with with our paramters and N and return something
}
int main()
{
  int n;
  std::cin >> n;
  // Limit n to a certain amount of values
  Foo<N>(params);
}

The first question you might ask is why? If N is dynamic, why not just pass it in as a parameter? One of the biggest reasons is performance (assuming this is critical to the function). By providing N as a template parameter, the compiler can optimize the function call for that value N by unrolling loops, restructuring code, memory optimization, etc.

A personal use case for this was trying to implement image processing convolutions on a GPU for many of my projects. I wanted to have dynamically sized convolution kernals that could be adjusted at compile time. Comparing the performance of using simulated dynamic template parameters vs dynamic width parameters resulted in optimizations of 2-10x processing time reduction (especially when my widths were small).

Another good example of this is the FFT Example by Dr Dobbs where by using the length as a template parameter, and template metaprogramming, a recursive FFT can have really nice performance!

Simulating dynamic parameters

The best way to simulate this is by having a switch statement that compares the dynamic parameter and calls the appropriately template parameter. Ie FooDynamic(n) calls FooTemplate (). An example of this is

template<int N> SomeReturnType FooDynamic(n, parameters)
{
  if ( n == 1 ) FooTemplate<1> (parameters);
  if ( n == 2 ) FooTemplate<2> (parameters);
  if ( n == 3 ) FooTemplate<3> (parameters);
  .
  .
  .
}

Note, X Macros are a perfectly good solution to this. However we can use FOR_EACH as well. Furthermore, with range we can avoid having to write our values explicitly. Lets say we wanted to allows the parameters from n = 1 to 10. The following code becomes

template<int N> SomeReturnType FooDynamic(n, parameters)
{
#define DYNAMIC_TEMPLATE_CALL(x) if (n == x) FooTemplate<x> (parameters);
  // Expands to if statements for all the values
  FOR_EACH(DYNAMIC_TEMPLATE_CALL, RANGE(1, 10))
#undef DYNAMIC_TEMPLATE_CALL
}

Leave a Reply

Your email address will not be published. Required fields are marked *