Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add an argument to a va_list

I have the following code:

int __dmasprintf (char **s, const char *format, ...) {

    char buf[512];
    va_list arg;
    int ret;
    va_start(arg,format);
    ret = vsprintf(buf, format, arg);
    va_end(arg);

    *s = strdup(buf);
    if (*s == NULL) return -1;
    return 0;   
}

I want to add an argument to the va_list arg before calling vsprintf() because my format contains 1 extra argument at the end.

How to add an argument (for example char * myarg) to the the va_list arg?

Or is it possible to pass vsprintf() a customized list?

like image 489
MOHAMED Avatar asked Jul 30 '15 14:07

MOHAMED


People also ask

Is Va_start is a macro?

The va_start macro enables access to the variable arguments following the named argument parm_n . va_start should be invoked with an instance to a valid va_list object ap before any calls to va_arg.

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.


2 Answers

You can't.

You either need to make two vsprintf (surely vsnprintf?) calls, or replace your function with a variadic macro, like

#define __dmasprintf(S, FMT, ...) ( \
    (*S = do_dmasprintf(FMT, __VA_ARGS__, my_arg)) == NULL ? -1 : 0)

char *do__dmasprintf (const char *format, ...) {

    char buf[512];
    va_list arg;
    int ret;
    va_start(arg,format);
    ret = vsnprintf(buf, sizeof(buf), format, arg);
    va_end(arg);

    char *s = strdup(buf);
    return s;   
}

Notes:

  1. I replaced vsprintf with vsnprintf. There's no reason to use the former here (or pretty much anywhere else)
  2. you ignore ret. Should you?
  3. I kept the macro argument layout similar to the original, but since __VA_ARGS__ must be one-or-more arguments (it can't be empty), that means at least one argument is required after FMT. Just remove the FMT argument entirely if you want to allow zero arguments after it.
like image 186
Useless Avatar answered Sep 25 '22 07:09

Useless


There is unfortunately no direct way to do that. There is a reason for that : the stdarg macros take the address in the stack of the last known parameter, and then directly iterate the stack.

If you can use macros, @Useless provided a nice solution - beware, macros can have side effects when you pass variables pre- or post-fixed with ++ or --.

If you want to avoid macros, you will have to write your own variant of vsprintf. No problem for it, just find a source for C stdlib (GNU libc could be a nice start point) and be brave ... hope you can use macros !

like image 20
Serge Ballesta Avatar answered Sep 23 '22 07:09

Serge Ballesta