Poornima M
Poornima M

Reputation: 1

Passing variable args from macro to a function that expects va_list

#define exampleA(buf, args...) \
        exampleB(buf, ##args); \
    }

#endif

works where the exampleB function declaration is exampleB(char* buf, ...). But I need to change the declaration as exampleB(char* buf, va_list args). How can change the marco accordingly?

Upvotes: 0

Views: 500

Answers (2)

rici
rici

Reputation: 241671

You cannot. In order to create a va_list, you need to have a variadic function. There is no portable facility to create a va_list object from a list of arguments.

It is quite common to write variadic functions in pairs: the outer function uses the facilities in <stdarg.h> to construct a va_list, and it passes that list directly to the function which does the work. You see that in the standarid I/O library, for example: printf/vprintf, fprintf/vfprintf, snprintf/vsnprintf, scanf/vscanf, etc.

And that's what you should do for your problem: write exampleB as the va_list form of exampleA.

Here, for example, is the entire implementation of the fprintf function extrated from the FreeBSD library, which can serve as a model of this style:

int
fprintf(FILE * __restrict fp, const char * __restrict fmt, ...)
{
    int ret;
    va_list ap;

    va_start(ap, fmt);
    ret = vfprintf_l(fp, __get_locale(), fmt, ap);
    va_end(ap);
    return (ret);
}

As you can see from that snippet, vfprintf_l (which takes a locale argument) is actually the implementation; all the other *printf functions are simple wrappers. (printf, for examples, calls vfprintf_l passing stdin and the locale argument.) That avoids having to traverse multiple wrappers in a single call, although modern optimising compilers are usually able to do that elimination automatically.

(See lib/libc/stdio/fprintf.c for the full file in the FreeBSD source repository mirror on Github.)

Speaking of portability, #define exampleA(buf, args...) is a GCC extension which is not portable to most C implementations (although Clang implements it as well). You should use __VA_ARGS__ as per standard C:

#define exampleA(buf, ...) \
        exampleB(buf, ## __VA_ARGS__)

Even better, if you can, replace the other GCC extension (, ##) with the newer standard __VA_OPT__:

#define exampleA(buf, ...) \
        exampleB(buf __VA_OPT__(,) __VA_ARGS__)

Upvotes: 5

mrksngl
mrksngl

Reputation: 109

I don't think this is possible the way you imagine it. Let's consider a function exampleX(char *,...): the arguments you pass must live somewhere and let's say they are pushed to the stack. The va_list macros know that and can access them from there. The moment you leave that function, the arguments cannot be assumed to be on the stack anymore.
Thus, you may not write a helper function like va_list getArgs(char*x,...) { /* use va_start and return the va_list */ } and pass that va_list to exampleB.

The only thing I can think of is to write a trampoline function such as

void callExampleB(char * buf, ...) {
  va_list ap;
  va_start(ap, buf);
  exampleB(buf, ap);
  va_end(ap);
}

and call that in your macro.

Upvotes: 2

Related Questions