Last Update:
Variadic Templates and a Factory Function
Table of Contents
Variadic Templates from C++11 is probably not a feature that you use on a daily basis. But recently, I’ve come across one refactoring example where I’ve decided to give a try and apply variadics.
Intro
When I was doing some work in some old UI code I’ve noticed several similar lines of code that looked like that:
switch (ControlID)
{
case ControlA:
if (Message == MSG_CLICK)
{
FirstMenu *pMenu = new FirstMenu("Test");
pMenu->Show();
// pMenu is stored in manager and then deleted later...
// so no need to delete it here
}
break;
case OtherControl
{
// code here...
break;
}
case ControlB:
if (Message == MSG_CLICK)
{
SecondMenu *pMenu = new SecondMenu();
pMenu->Show();
}
break;
}
In the above example code for that shows FirstMenu
and SecondMenu
is
very similar. How can we refactor that into simpler code?
We could go deeper and change the order of this switch statement, but for now, let’s focus only on the menu handling.
I’ve decided that we could wrap the whole test for the click message and a menu invocation into one method. We could use templates or polymorphic types. But then I’ve noticed that one menu has an additional parameter for the constructor. So my simple method wouldn’t work.
So here we need to look at Variadic Templates!
Full code can be found here: http://coliru.stacked-crooked.com/a/d5edfea9c74e7b3c
Improved version
I’ve come up with the following method:
template <typename TMenu, typename... TArg>
void ShowMenuOnClick(MSG Message, TArg&&... Args)
{
if (Message == MSG::Click)
{
TMenu* pMenu = new TMenu(forward<TArg>(Args)...);
pMenu->Show();
}
}
To be honest, I’ve just copied code from
make_unique
’s
implementation :)
How does it work then?
The construction allows passing a variable number of arguments into a template function. This is what I needed for the refactored code: one menu requires one attribute, another menu is constructed without anything:
The code can be now changed into:
switch (ControlID)
{
case ControlA:
ShowMenuOnClick<FirstMenu>(Message, "Test");
break;
case OtherControl:
{
// code here...
break;
}
case ControlB:
ShowMenuOnClick<SecondMenu>(Message);
break;
}
Great! We’ve replaced duplicated code with just one line of helper method call.
What’s more, the code should work as before so it’s a good sign :)
Details
First of all, you might wonder why Variadic Templates were needed. Before C++11 if your template code required several parameters you would manually write each declaration/definition on your own. So for example if a template might use up to 4 parameters, you would declare four different options like
template <class T1>
void Func(T1);
template <class T1, class T2>
void Func(T1, T2);
template <class T1, class T2, class T3>
void Func(T1, T2, T3);
template <class T1, class T2, class T3, class T4>
void Func(T1, T2, T3, T4);
With variadics, you can wrap it into one or two declarations (depending if you want some recursion to happen).
Structure
typename... Args
is called a template parameter pack.Args... args
is called a function parameter pack. Each argument might have different type.
The real benefit of having variadics is that we might have functions that take an arbitrary number of parameters and this will be evaluated at compile time! This could be achieved in the past, but with a lot of effort. Now the code can be much more expressive.
Of course, Variadic templates is not the only method you could use: var
args from C are well-known technique used especially in printf
style
functions. They work quite well (by taking benefit from various stack
manipulation tricks), but the logic is executed at runtime, so this
costs something. With Variadics we might create a type-safe printf
,
which takes no time at runtime…
Read more about variadics templates in the section 3.10 of te book Discovering Modern C++.
forward what?
In the variadic function, I’ve also used another thing that should be explained. Although all rules are quite confusing about rvalues/lvalues/xvalues… but for our code, it’s worth to remember the following thing:
Since we’ve used rvalue reference (&&
) - to be correct, we might even
say it’s a universal reference since there’s type deduction happening -
we cannot change the type that is passed to internal function calls.
Those references have names in the context of ShowMenuOnClick
so they
now become regular lvalue references. To pass them with unchanged type
we have to wrap them into std::forward
.
If you’re curious, there’s a whole section in the book Effective Modern C++ - 5th section, items from 23 till 30 about rvalue references and move semantics.
Summary
Variadic Templates offers a way to specify a function or class that
accepts a variable number of arguments. All the work happens at compile
time, so there’s no cost at runtime. This feature opens a lot of
possibilities like type-safe printf
, classes similar to touples or
“factory” function that was presented in this post.
- Do you use Variadic Templates?
- In what areas they are they are especially useful?
BTW: If you liked this article, please sign up for my free newsletter.
References
I've prepared a valuable bonus for you!
Learn all major features of recent C++ Standards on my Reference Cards!
Check it out here: