Variadic Template / Function Arguments
Variadic functions are those that accept a variable number of arguments.
C++
Resources
Variadic Template
A variadic template is a class or function template that supports an arbitrary number of argument.
template<typename... Args> // parameter pack -? ... Args
void f(Args... args) {} // pack expansion -> pattern: Args
- A parameter pack is introduced by an identifier Args prefixed by an ellipsis…
- A parameter pack can later be expanded by an ellipsis …
- A pack expansion is equivalent to a comma-separated list of instances of the pattern
A pattern is a set of tokens containing the identifiers of one or more parameter packs.
Resources
- https://www.geeksforgeeks.org/variadic-function-templates-c/
- https://learn.microsoft.com/en-us/cpp/cpp/ellipses-and-variadic-templates?view=msvc-170
- https://federico-busato.github.io/Modern-CPP-Programming/11.Templates_II.pdf
Example of useful variadic template class:
Tuple
(can hold multiple types)
But most of the time, seems to be used for variadic funtions.
Basic example (from Modern C++)
template<typename T, typename R>
auto add(T a, R b) {
return a + b;
}
template<typename T, typename... Args>
auto add(T a, Args... args) {
return a + add(args...);
}
Since C++17, you can do this
Unary/Binary folding
template<typename... Args>
auto add_unary(Args... args) { // Unary folding
return (... + args); // unfold: 1 + 2.0f + 3ull
}
template<typename... Args>
auto add_binary(Args... args) { // Binary folding
return (1 + ... + args); // unfold: 1 + 1 + 2.0f + 3ull
}
add_unary(1, 2.0f, 3ll); // returns 6.0f (float)
add_binary(1, 2.0f, 3ll); // returns 7.0f (float)
The number of variadic arguments can be retrieved with the sizeof… operator
sizeof...(args) // e.g. 3
For example, when you use make_shared
and you pass in the constructor arguments,
template <typename T, typename... Args>
std::shared_ptr<T> make_shared(Args&&... args) {
// Allocate and construct an object of type T using the provided arguments.
return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
Minor quirks
When a pattern contains more than one parameter pack, all packs must have the same length.
- Though you probably will never run into this
template<typename... A, typename... B>
void foo(A... a, B... b) {
// Code that tries to use both parameter packs together
}
C-Style Variadic
There’s another way, which I found out through chatgpt which is to use <cstdarg>
(this is c-style variadic functions)
#include <iostream>
#include <cstdarg>
void variadicFunction(int num, ...) {
va_list args;
va_start(args, num);
for (int i = 0; i < num; ++i) {
int value = va_arg(args, int); // Get next argument, specifying the type
std::cout << value << std::endl;
}
va_end(args);
}
int main() {
variadicFunction(3, 10, 20, 30); // First argument is the count of subsequent arguments
return 0;
}
Python
In Python, this is done using the *args
and **kwargs
.
def variadic_function(*args):
for arg in args:
print(arg)
variadic_function(1, 2, 3)
def variadic_function(*args, **kwargs):
print("Positional arguments:", args)
print("Keyword arguments:", kwargs)
variadic_function(1, 2, 3, name="Alice", age=30)
The kwargs
is used for those with keyword arguments
Positional arguments: (1, 2, 3)
Keyword arguments: {'name': 'Alice', 'age': 30}