Sauhaarda Chowdhuri
Sauhaarda Chowdhuri

Reputation: 313

Include First Argument in va_list object

Is it possible to include the first argument of a function within a va_list? I want to pass the arguments from one function to another function.

int main(){
    test(1,2,3,4,5,-1);
}

void test(int firstArg, ...){
    va_list va;
    va_start(va, firstArg);
    passFunction(va);
}

void passFunction(va_list va){
    int x;    
    while((x = va_arg(ap,int))!=-1){
        cout << x << endl;  
    }
}

Output I Want:

1
2
3
4
5

Actual Output:

2
3
4
5

Is there any way to achieve this without explicitly passing the first argument to the function?

Upvotes: 3

Views: 2548

Answers (2)

Zoccadoum
Zoccadoum

Reputation: 872

I know this is an old question, but I came across it recently and thought I would share my solution. This answer should work regardless of the compiler, but adds a little more code. I use the first parameter in the while statement but I don't call va_arg() until I've consumed the first parameter.

 main () {
        test( 1, 2, 3, 4, 5, -1 );
    }

void passFunction( int one, va_list va )
{
    int x = one;
    va_list va_copy;
   
    va_copy( va_copy, va );
    while (x != -1)
    {
        std::cout << x << std::endl;
        x = va_arg( va_copy, int );        
    }
    va_end( va_copy );
}

void test( int one, ... ) const
{
    va_list va;
    va_start(va,one);
    passFunction( one, va );
    va_end(va);
}

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 595981

va_start() requires a fixed parameter and initializes the va_list to the next parameter following the specified parameter. Since your function only has one fixed parameter, there is no option to include it in the va_list using the stock va_start() because there is no fixed parameter in front of it.

In 32-bit only (64-bit is MUCH more complex), you can copy your compiler's source code for va_start() from varargs.h or stdargs.h (or equivalent code) into your own source file and then tweak it to initialize the va_arg with the address of the specified parameter itself rather than the address that follows it.

For example, in C++Builder, va_start() is defined as:

#ifdef __cplusplus
#define va_start(ap, parmN) ((void)((ap) = (std::va_list)((char _FAR *)(&parmN)+__size(parmN))))
#else
#define va_start(ap, parmN) ((void)((ap) = (va_list)((char _FAR *)(&parmN)+__size(parmN))))
#endif

So you would just remove the +__size(parmN) portion:

#ifdef __cplusplus
#define my_va_start(ap, parmN) ((void)((ap) = (std::va_list)((char _FAR *)(&parmN))))
#else
#define my_va_start(ap, parmN) ((void)((ap) = (va_list)((char _FAR *)(&parmN))))
#endif

In Visual Studio, va_start() is defined as:

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INSIZEOF(v) )
#define va_start _crt_va_start

So you would just remove the + _INSIZEOF(v) portion:

#define my_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) )

GCC actually uses compiler intrinsic functions instead of macros, so you probably can't get its va_start() source, but the generated code should be similar since the cdecl calling convention (the only calling convention that supports variadic parameters in 32bit) is fairly standardized across compilers.

Either way, the code generated by the preprocessor will end up roughly the same, so you could just code it directly, if it is compatible with your compiler:

void test(int firstArg, ...){
    va_list va = (va_list) &firstArg;
    passFunction(va);
}

Upvotes: 0

Related Questions