How to use variadic templates (parameter packs) in C++ to pass a variadic list of arguments to a sub-function
Although you can do this in both C and C++ using variadic functions with va_list
, va_start()
, va_arg()
, and va_end()
, it is much cleaner and easier to do it in C++ using variadic templates (parameter packs) instead.
The secret is to allow a generic function to be passed in, of any format, via typename FuncType
, and to allow a variadic list of arguments to be passed in via typename... FuncArgs
. The template specifier will therefore be template<typename FuncType, typename... FuncArgs>
. You then pass the function name to the outer function as FuncType innerFunc
, and you pass the list of variadic arguments to the outer function as FuncArgs... args
. Inside the template function, the list of arguments can then be passed to a subfunction as args...
, like this: innerFunc(args...);
.
Here is the whole thing in context:
// INNER FUNCTIONS TO PASS TO AN OUTER FUNCTION
void print1(int i)
{
printf("print1: %i\n", i);
}
void print2(double d, int i)
{
printf("print2: %f, %i\n", d, i);
}
void print3(int i, double d, const std::string& str)
{
printf("print3: %i, %f, %s\n", i, d, str.c_str());
}
// OUTER FUNCTION
template<typename FuncType, typename... FuncArgs>
void OuterFunc(FuncType innerFunc, FuncArgs... args)
{
printf("OuterFunc start.\n");
// Call the inner function with all passed-in args!
printf("Calling inner function with all passed-in args.\n");
innerFunc(args...);
printf("OuterFunc end.\n\n");
}
int main()
{
OuterFunc(print1, 100);
OuterFunc(print2, 99.1234, 77);
OuterFunc(print3, 123, 10.55, "hey you!");
return 0;
}
Full, runnable example, with comments:
variadic_templates_parameter_packs_and_functions.cpp from my eRCaGuy_hello_world repo:
// C++ includes
#include <cstdint> // For `uint8_t`, `int8_t`, etc.
#include <cstdio> // For `printf()`
#include <iostream> // For `std::cin`, `std::cout`, `std::endl`, etc.
#include <string>
// -------------------- Some inner functions to choose from START -------------------
void print1(int i)
{
printf("print1: %i\n", i);
}
void print2(double d, int i)
{
printf("print2: %f, %i\n", d, i);
}
void print3(int i, double d, const std::string& str)
{
printf("print3: %i, %f, %s\n", i, d, str.c_str());
}
// -------------------- Some inner functions to choose from END ---------------------
// The outer function, which is a variadic template, containing one `typename...` parameter pack.
// See: https://en.cppreference.com/w/cpp/language/parameter_pack
template<typename FuncType, typename... FuncArgs>
void OuterFunc(FuncType innerFunc, FuncArgs... args)
{
printf("OuterFunc start.\n");
// Call the inner function with all passed-in args!
printf("Calling inner function with all passed-in args.\n");
// See the "Expansion loci" section of this documentation here:
// https://en.cppreference.com/w/cpp/language/parameter_pack
// This is really cool, because calling the inner function like this is **just like the Python
// example here!**: https://stackoverflow.com/a/803632/4561887--except you pass the arguments
// to the inner function as `args...` in C++ here instead of as `*args` (the contents of the
// arguments list) in Python.
innerFunc(args...);
printf("OuterFunc end.\n\n");
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Demonstrate variadic templates (parameter packs) in C++!\n\n");
OuterFunc(print1, 100);
OuterFunc(print2, 99.1234, 77);
OuterFunc(print3, 123, 10.55, "hey you!");
return 0;
}
Sample build and run command, and output:
eRCaGuy_hello_world/cpp$ time g++ -Wall -Wextra -Werror -O3 -std=c++17 variadic_templates_parameter_packs_and_functions.cpp -o bin/a && bin/a
real 0m0.281s
user 0m0.245s
sys 0m0.036s
Demonstrate variadic templates (parameter packs) in C++!
OuterFunc start.
Calling inner function with all passed-in args.
print1: 100
OuterFunc end.
OuterFunc start.
Calling inner function with all passed-in args.
print2: 99.123400, 77
OuterFunc end.
OuterFunc start.
Calling inner function with all passed-in args.
print3: 123, 10.550000, hey you!
OuterFunc end.
References
- Multiple typename arguments in c++ template? (variadic templates)
- cppreference.com: variadic templates (parameter packs)