Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

length of va_list when using variable list arguments?

Tags:

c++

c

Is there any way to compute length of va_list? All examples I saw the number of variable parameters is given explicitly.

like image 559
Anton Kazennikov Avatar asked Apr 08 '10 07:04

Anton Kazennikov


People also ask

What is Va_list 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.

Can Va_list be null?

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.

How many arguments can a function receive?

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.


2 Answers

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.

like image 104
Motti Avatar answered Oct 06 '22 14:10

Motti


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

like image 26
mbolt35 Avatar answered Oct 06 '22 12:10

mbolt35