In C, we know we have something called printf() function. This printf() function can take any number of arguments of any built-in type (not user-defined type). As an example, the following piece of code is valid for the standard built-in printf() function.
<Code>
const char* msg = "%s can accept %i parameters (or %s).";
printf(msg, std::string("Variadic templates"), 100, "more");
printf(msg, std::string("Variadic templates"), 100, "more");
</Code>
The problem in this code is, it will not print the string part, but it gets compiled with the MSVC as well as GCC 8.2 compiler.
However, if I change the code like below, now it prints everything properly.
<Code>
const char* msg = "%s can accept %i parameters (or %s).";
printf(msg, std::string("Variadic templates").c_str(), 100, "more");
</Code>
The printf is like a variadic template function but it sticks to only built-in types. Now the question comes how can I write variadic template functions in C++. In C++ 11 the support has been added.
Let's start with a simple objective, which is to create a function that takes any arbitrary number of parameters of any arbitrary type and just prints them on the screen. As an example, the function shall look like below:
PrintAllTypes(1, 'C', "Hello World", 0.67, 1.002);
Here goes the code for PrintAllTypes:
<Code>
void PrintAllType() { std::cout << "\n"; } /* This is the base case */
template<typename T, typename... Rest>
void PrintAllType(const T& current, const Rest&... rest) {
std::cout << current << " ";
PrintAllType(rest...); // This is a recursive call using pack expansion syntax
}
</Code>
Below is another variant of Print that takes any number of parameters but is limited to numbers only.
<Code>
void Print() { std::cout << "\n"; } /* This is the base case */
template <typename T,
typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr, typename... Rest>
void Print(const T& current, const Rest&... rest) {
std::cout << current << " ";
Print(rest...); // Again, this is a recursive call using pack expansion syntax
}
</Code>
Let's try the addition of numbers via the variadic template function:
<Code>
template <typename T>
auto AddGeneric(T& t)
{
return t;
}
template<typename T, typename... Rest,
typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
auto AddGeneric(T& current, Rest&... rest)
{
return current + AddGeneric(rest...);
}
// The Client code:
int x = 10, y = 20; double dbl = 5.5;
auto res = AddGeneric(x, dbl, y);
PrintAllType("Add Result: ", res);
</Code>
The output:
Add Result: 35.5
What if I have a number array and I need to use that array as an argument in a variadic function. One possibility could be using a functor to unpack the array and then use it for a variadic function.
<Code>
template <typename T>
auto AddGeneric(T& t)
{
return t;
}
template<typename T, typename... Rest,
typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
auto AddGeneric(T& current, Rest&... rest)
{
return current + AddGeneric(rest...);
}
/* Array passed to a variadic function. With the help of a functor unpacking the array */
template<typename Functor, typename T, std::size_t N>
auto AdditionByArray(Functor fn, T(&t)[N])
{
return AdditionByArrayImpl(fn, t, std::make_index_sequence<N>{});
}
template<typename F, typename T, std::size_t N, std::size_t... idx>
auto AdditionByArrayImpl(F fn, T(&t)[N], std::index_sequence<idx... >)
{
return fn(t[idx]...);
}
struct AddSupli
{
template <typename... Arguments>
auto operator()(Arguments... args)
{
return AddGeneric(args...); // This is same recursive call
}
};
</Code>
So far so good. Will come back to this topic later.
Reference: Ellipsis and variadic templates
Comments