Thursday, March 11, 2010

C++-201x Variadic Templates

C++-201x (or C++-0x as near sighted individuals like to call it) introduced a new feature called variadic templates, which allows a template class or function to recieve multiple parameters to it of various types as you would normally achieve with "...".

You could continue to use stdarg if you wish, but a limitation of it is that you have no way of knowing what type each parameter is to use with va_arg(), unless you just hard code it to only work with a specific type, or pass a type list how the printf() family of functions do it, or something similar.

I always wanted a way to do func(integer, string, float, whatever), and interchange that with func(float, whatever, char), and now you can.

If you try to research this topic online, basically everyone is just writing and talking about how to implement a tuple class, or how to make a type safe printf. Lets try something a little more interesting and perhaps instructive, yet simple.

As can be seen I achieved my desired goals of being able to pass various types, not know the types to be passed in advance, and not need a type list passed anywhere. All the other examples I've seen seem to be forgetting these important points.

As for how this works "typename ...P" defines a template type of P which will be many parameters packed into a single variable. "P ...p" receives all these parameters in a variable named "p".

However the only things I can really do is see how many parameters are in "p" using "sizeof...(p)", or split "p" up into all its parameters, to then pass to a function by using "p...". It would be nice to be able to simply iterate through the values of "p", but that's not possible at the moment.

Using standard recursion techniques, you can pass the split up parameters from "p" to a function which has enough templated parameters as "p" is currently holding, or a lesser amount, and the last parameter can be a variadic parameter to catch all the others which don't fit into the first few. Then using those first parameters, you can access the values you need, and so on ad infinitum.

Read up on recursion if you're confused.

Now if you're still wondering what this may be useful for, just consider functions which handle data serialization or something similar.

One thing to note, at least with GCC at the moment, you always need to have a separate function as a base case, even if it's never used.

Take "output('5', 2, "cows", -1, 0.5f, 16.3);", on the last leg of iteration, when the double is passed to the first parameter to be received by "t", and "p" is empty, "if (sizeof...(p)) { output(p...); }" which is the line responsible for the recursion seeing an empty "p" won't ask for "output()", but it seems the compiler still wants it to exist:

Insane Coder, unfortunately, the base case IS always necessary. Though the code path can never actually get inside the if statement, the function output(p...) when p is empty translates into just output(), which isn't handled by your variadic template. So the compiler takes "if(sizeof...(p)) { output(p...); }" and turns it into "if(0) { output(); }" and while a good optimizer can and should optimize that out, if statements are still technically considered to be runtime operations, hence, the function being called has to exist in order to be linked properly in case it isn't optimized out. In response to Erik, a better way to do it might be:

templatevoid output(T t, P ...p){cout << t << ' ';output(p...);}

void output() { cout << '\n'; }

which is the same code minus the if-else with no code repition (whereas you have "cout << t" twice).

That code actually places all the parameters neatly into a tuple, and attempts to iterate over it. But alas, it won't compile:test.cpp: In function ‘void output(P ...)’:test.cpp:12: error: ‘i’ cannot appear in a constant-expression

Templates always need recursion, and no trick I can think of at the moment would allow for iteration.