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)