Is there any way to compute length of va_list
? All examples I saw the number of variable parameters is given explicitly.
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.
NULL in general case is not a valid initializer for a va_list object. So, the answer to your question is: it is not possible.
Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero. The maximum number of arguments (and corresponding parameters) is 253 for a single function.
There is no way to compute the length of a va_list
, this is why you need the format string in printf
like functions.
The only functions macros available for working with a va_list
are:
va_start
- start using the va_list
va_arg
- get next argument va_end
- stop using the va_list
va_copy
(since C++11 and C99) - copy the va_list
Please note that you need to call va_start
and va_end
in the same scope which means you can't wrap it in a utility class which calls va_start
in its constructor and va_end
in its destructor (I was bitten by this once).
For example this class is worthless:
class arg_list { va_list vl; public: arg_list(const int& n) { va_start(vl, n); } ~arg_list() { va_end(vl); } int arg() { return static_cast<int>(va_arg(vl, int); } };
GCC outputs the following error
t.cpp: In constructor
arg_list::arg_list(const int&)
:
Line 7: error:va_start
used in function with fixed args
compilation terminated due to -Wfatal-errors.
One approach that hasn't been mentioned yet is to use a pre-processor macro to call the variadict function using the va_list length as the first parameter and also forward along the arguments. This is somewhat of a "cute" solution, but does not require manually inputting the argument list length.
Assume you have the following function:
int Min(int count, ...) { va_list args; va_start(args, count); int min = va_arg(args, int); for (int i = 0; i < count-1; ++i) { int next = va_arg(args, int); min = min < next ? min : next; } va_end(args); return min; }
The idea is that you have a preprocessor macro capable of counting the number of arguments by using a mask for the __VA_ARGS__
. There are a few good preprocessor libraries for determining the __VA_ARGS__
length including P99 and Boost Preprocessor, but just so I don't leave holes in this answer, here's how it can be done:
#define IS_MSVC _MSC_VER && !__INTEL_COMPILER /** * Define the macros to determine variadic argument lengths up to 20 arguments. The MSVC * preprocessor handles variadic arguments a bit differently than the GNU preprocessor, * so we account for that here. */ #if IS_MSVC #define MSVC_HACK(FUNC, ARGS) FUNC ARGS #define APPLY(FUNC, ...) MSVC_HACK(FUNC, (__VA_ARGS__)) #define VA_LENGTH(...) APPLY(VA_LENGTH_, 0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #else #define VA_LENGTH(...) VA_LENGTH_(0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #endif /** * Strip the processed arguments to a length variable. */ #define VA_LENGTH_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
Note: A lot of the noise from above is work-around support for MSVC.
With the above defined, you can create a single macro to perform all length based operations:
/** * Use the VA_LENGTH macro to determine the length of the variadict args to * pass in as the first parameter, and forward along the arguments after that. */ #define ExecVF(Func, ...) Func(VA_LENGTH(__VA_ARGS__), __VA_ARGS__)
This macro is capable of calling any variadict function as long as it begins with the int count
parameter. In short, instead of using:
int result = Min(5, 1, 2, 3, 4, 5);
You can use:
int result = ExecVF(Min, 1, 2, 3, 4, 5);
Here's a template version of Min which uses the same approach: https://gist.github.com/mbolt35/4e60da5aaec94dcd39ca
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