BabelFish
BabelFish

Reputation: 909

How to determine if va_list is empty

I have been reading that some compilers support va_list with macros and users were able to overload the functionality with other macros in order to count the va_list.

With visual studio, is there a way to determine if the va_list is empty (aka count==0)? Basically I would like to know this condition:

extern void Foo(const char* psz, ...);
void Test()
{
  Foo("My String"); // No params were passed
}

My initial thought was to do something like this:

va_list vaStart;
va_list vaEnd;
va_start(vaStart, psz);
va_end(vaEnd);
if (vaStart == vaEnd) ...

The problem is that va_end only sets the param to null.

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

I was thinking of maybe incorporating a terminator but I would want it to be hidden from the caller so that existing code doesnt need to be changed.

Upvotes: 8

Views: 11127

Answers (3)

Michael Haephrati
Michael Haephrati

Reputation: 4225

I am aware that my answer isn't an "orthodox" answer, however due to the limitation of the va_list macro, I found the following solution to work. We can look at a va_list as an array of chars, and even better, a null terminated one, so you can try

va_list argptr;
if(strcmp(argptr,"")) 
{
   // not empty
}
else
{
   // empty
}

I tried that and it worked for me. I used Visual Studio 2013 Ultimate. The project type is Win32 Console Application. No compilation errors.

Upvotes: -3

Mark Benningfield
Mark Benningfield

Reputation: 2892

I realize this question is fairly old, but I thought it might be helpful to flesh it out a bit. As Mike Seymour answered quite correctly, there is no absolutely reliable way to determine the number of arguments in a va_list. That's why the conventional way to define a variadic function is to include a parameter that has that information, like so: void func(const char *str, int count, ...);, which defines a contract your callers are supposed to abide by.

EDIT: The standard (7.16.1.1.3) is actually silent on the value returned by va_arg(vl, type) for any call past the end of the variable argument list. The most common case for most types is typed-zero. However, CAVEAT EMPTOR - it doesn't have to be.

The value returned from va_arg(vl, type) when there are no more arguments is a typed-zero value. For numeric types, it is 0. For pointers, it is a NULL pointer. For structs, it is a struct with all fields zeroed. If you elect to copy the va_list and try to count the copy, like so:

void func(const char *str, ...) {
    va_list vl;
    va_list vc;
    int x;
    int count;

    count = 0;
    va_start(vl, str);
    va_copy(vc, vl);
    do {
        x = va_arg(vc, int);
        if (x == 0) break;
        count++;
    } while (1)
    va_end(vc);
    .
    .
    . // do something, or something else,
    . // based on the number of args in the list
    .
    va_end(vl);

You would have to make the assumption that the caller would abide by the contract not to pass a NULL or zero value in the list. Either way, you have to understand that the caller of a variadic function is responsible for abiding by the stated contract. So write your function, publish the contract, and sleep easy.

Upvotes: 0

Mike Seymour
Mike Seymour

Reputation: 254451

There is no way to tell how many arguments are passed through ..., nor what type they are. Variadic function parameters can only be used if you have some other way (e.g. a printf-style format string) to tell the function what to expect; and even then there is no way to validate the arguments.

C++11 provides type-safe variadic templates. I don't know whether your compiler supports these, or whether they would be appropriate for your problem.

Upvotes: 8

Related Questions