In C, the only way to define a variable length of arguments is declaring its prototype with a ellipsis and use va_list
, va_start
, va_arg
, va_end
to extract them. Just like the printf
series and the scanf
series do.
In C++11, a new approach was introduced as below.
template <typename T, typename... MoreT>
void func(T arg, MoreT... args){
// Do some stuff
func(args);
}
What are the benefits and downsides of each method? Is either of them discouraged to be used or encouraged in C++?
va_list is a complete object type suitable for holding the information needed by the macros va_start, va_copy, va_arg, and va_end. If a va_list instance is created, passed to another function, and used via va_arg in that function, then any subsequent use in the calling function should be preceded by a call to va_end.
typename... Args is called a template parameter pack, and Args... args is called a function parameter pack (Args is, of course, a completely arbitrary name and could be anything else).
Description. The C library macro void va_start (va_list ap, last_arg) initializes ap variable to be used with the va_arg and va_end macros. The last_arg is the last known fixed argument being passed to the function i.e. the argument before the ellipsis. This macro must be called before using va_arg and va_end.
Yes. VA accepts any evidence that a Veteran or his/her accredited representative chooses to submit in support of a claim. Once all evidence is received, VBA claims processors review and weigh the evidence overall as part of the decision-making process.
The va_list type is an array containing a single element of one structure containing the necessary information to implement the va_arg macro. The C definition of va_list type is given in figure below
To supplement VHA’s internal capacity to conduct C&P exams, VBA has contracted with three primary vendors to provide exams across the country. This has expanded the agency’s ability to conduct exams in more places than just traditional VA Medical Centers and Outpatient Clinics.
C style variadic functions are heavily discouraged in C++. Styles vary but writing those kinds of functions will get you stoned in some circles (including mine), unless there's a truly exceptional reason.
As far as the trade-offs go, C style variadic functions are completely type unsafe. You could try to extract something from the variadic pack as the wrong type, which will result in a segfault. C++ variadic templates are strongly typed so this is not possible (unless of course you absolutely force it with a reinterpret_cast or something like that).
Beyond that, the C++ code will also typically (there are rare exceptions due to code bloat) perform better at run time. There's less indirection, more information for the compiler to work with. However, there may be longer compiler times, especially since variadic template functions (like all templates) generally must be defined in the header file, whereas C style variadics can be defined in the .cpp file.
In most C or C++ code (which is written for very high performance applications), the order of priorities would usually be correctness, then performance, then compile time. So most C++ devs believe that variadic templates are clear, clear winners here.
It is basically very similar to the comparison between a proper generic container in C++ using templates, and a void* based container in C++. Type safety + runtime performance, vs compile time performance (and .h vs .cpp).
C++ strive to be more type safe than C e.g. nullptr
, enum class
provide more type-safe code. Compiler is your more intelligent friend.
Variadic template allow you to be more specific about type at compile-time itself while arguments in va_list
et.al. C functions are evaluated at run-time
There aren't a lot of benefits to variadic parameter functions, but there are some.
If you use a C variadic function, you get one compiled function that handles all the cases. If you use a C++ function with variadic templates, you get one compiled function per parameter combination. If code size is a consideration for your project, this can be a problem.
C++ variadic templates are type-safe, whereas C variadic functions are not. This means that the compiler usually won't enforce the type of the parameters that a function passes to a variadic function. GCC and Clang support some attributes that address common cases:
__attribute__((format()))
tells the compiler that the variadic parameters use the printf
/scanf
convention, and will warn you when parameters don't match the format string that you've passed;__attribute__((sentinel(N)))
tells the compiler that the last variadic parameter should be the sentinel value.It's still very much possible to screw up. For instance, the open
function is variadic and requires a mask
parameter as a third parameter when O_CREAT
is specified, but it's frequently forgotten and neither of these __attribute__
extensions can address that.
The standard permits, but does not mandate, that implementations allow to pass non-POD types to C variadic functions; and when it does work, the semantics are implementation-defined. This means that you shouldn't do it if you want your code to be cross-platform.
Finally, it is typically harder to learn how to do C++ variadic templates right than it is to learn how to do C variadic functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With