CUDA 7 adds C++11 feature support to nvcc, the CUDA C++ compiler. This means that you can use C++11 features not only in your host code compiled with
nvcc, but also in device code. In my post “The Power of C++11 in CUDA 7” I covered some of the major new features of C++11, such as lambda functions, range-based for loops, and automatic type deduction (
auto). In this post, I’ll cover variadic templates.
There are times when you need to write functions that take a variable number of arguments: variadic functions. To do this in a typesafe manner for polymorphic functions, you really need to take a variable number of types in a template. Before C++11, the only way to write variadic functions was with the ellipsis (
...) syntax and the
va_* facilities. These facilities did not enable type safety and can be difficult to use.
As an example, let’s say we want to abstract the launching of GPU kernels. In my case, I want to provide simpler launch semantics in the Hemi library. There are many cases where you don’t care to specify the number and size of thread blocks—you just want to run a kernel with “enough” threads to fully utilize the GPU, or to cover your data size. In that case we can let the library decide how to launch the kernel, simplifying our code. But to launch arbitrary kernels, we have to support arbitrary type signatures. Well, we can do that like this:
template <typename... Arguments> void cudaLaunch(const ExecutionPolicy &p, void(*f)(Arguments...), Arguments... args);
Arguments... is a “type template parameter pack”. We can use it to refer to the type signature of our kernel function pointer
f, and to the arguments of
cudaLaunch. To do the same thing before C++11 (and CUDA 7) required providing multiple implementations of
cudaLaunch, one for each number of arguments we wanted to support. That meant you had to limit the maximum number of arguments allowed, as well as the amount of code you had to maintain. In my experience this was prone to bugs. Here’s the implementation of
cudaLaunch. Continue reading