Is there any way to compute length of va_list? All examples I saw the number of variable parameters is given explicitly.
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