359

Say I have a C function which takes a variable number of arguments: How can I call another function which expects a variable number of arguments from inside of it, passing all the arguments that got into the first function?

Example:

void format_string(char *fmt, ...);

void debug_print(int dbg_lvl, char *fmt, ...) {
    format_string(fmt, /* how do I pass all the arguments from '...'? */);
    fprintf(stdout, fmt);
 }
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
Vicent Marti
  • 7,203
  • 6
  • 30
  • 34
  • 4
    Your example looks a bit weird to me, in that you pass fmt to both format_string() and to fprintf(). Should format_string() return a new string somehow? – Kristopher Johnson Oct 15 '08 at 17:14
  • 2
    Example doesn't make sense. It was just to show the outline of the code. – Vicent Marti Oct 15 '08 at 17:22
  • 180
    "should be googled": I disagree. Google has a lot of noise (unclear, often confusing information). Having a good (voted up, accepted answer) on stackoverflow really helps! – Ansgar Feb 06 '09 at 09:53
  • 76
    Just to weigh in: I came to this question from google, and because it was stack overflow was highly confident that the answer would be useful. So ask away! – tenpn Feb 24 '09 at 12:18
  • 37
    @Ilya: if nobody ever wrote down stuff outside of Google, there would be no information to search for on Google. – Erik Kaplun Aug 29 '12 at 20:27

11 Answers11

229

To pass the ellipses on, you initialize a va_list as usual and simply pass it to your second function. You don't use va_arg(). Specifically;

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}
MarcH
  • 18,738
  • 1
  • 30
  • 25
SmacL
  • 22,555
  • 12
  • 95
  • 149
  • 3
    The code is taken from the question, and is really just an illustration of how to convert ellipses rather than anything functional. If you look at it `format_string` will hardly be useful either, as it would have to make in-situ modifications to fmt, which certainly shouldn't be done either. Options would include getting rid of format_string altogether and use vfprintf, but that makes assumptions about what format_string actually does, or have format_string return a different string. I'll edit the answer to show the latter. – SmacL Feb 24 '12 at 16:35
  • 1
    If your format string happens to use the same format string commands as printf, you can also get some compilers like gcc and clang to give you warnings if your format string isn't compatible with the actual arguments passed in. See the GCC function attribute 'format' for more details: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html. – Doug Richardson Nov 16 '13 at 19:00
  • 1
    This don't seems to work if you are passing the args twice in a row. – fotanus Jun 10 '15 at 04:20
  • 3
    @fotanus: if you call a function with the `argptr` and the called function uses the `argptr` at all, the only safe thing to do is call `va_end()` and then restart `va_start(argptr, fmt);` to reinitialize. Or you can use `va_copy()` if your system supports it (C99 and C11 require it; C89/90 did not). – Jonathan Leffler Jun 15 '15 at 01:32
  • 1
    Please note that @ThomasPadron-McCarthy 's comment is now out of date and the final fprintf is ok. – Frederick Sep 13 '16 at 20:19
63

There's no way of calling (eg) printf without knowing how many arguments you're passing to it, unless you want to get into naughty and non-portable tricks.

The generally used solution is to always provide an alternate form of vararg functions, so printf has vprintf which takes a va_list in place of the .... The ... versions are just wrappers around the va_list versions.

  • Note that [`vsyslog`](http://linux.die.net/man/3/vsyslog) is **not** [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/) compliant. – patryk.beza Aug 09 '16 at 12:29
53

Variadic Functions can be dangerous. Here's a safer trick:

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});
Community
  • 1
  • 1
Rose Perrone
  • 61,572
  • 58
  • 208
  • 243
  • 11
    Even better is this trick: `#define callVardicMethodSafely(values...) ({ values *v = { values }; _actualFunction(values, sizeof(v) / sizeof(*v)); })` – Richard J. Ross III Mar 03 '12 at 22:54
  • 5
    @RichardJ.RossIII I wish you would expand on your comment, it's hardly readable like this, I can't make out the idea behind the code and it actually looks very interesting and useful. – penelope Mar 30 '12 at 10:09
  • @penelope basically, it creates an array from the values passed in to the macro. It may only work on GCC, but I don't know for sure. – Richard J. Ross III Mar 30 '12 at 12:29
  • @RichardJ.RossIII No! No no no! Bad! Hack! Thou shalt not abuse the preprocessor in this way! Whenever there is a preprocessor and non-preprocessor way of accomplishing a single task, **always** pick the non-preprocessor way! Using the preprocessor is **always** a **hack** and **rarely necessary**. – ArtOfWarfare Jun 30 '13 at 19:00
  • 5
    @ArtOfWarfare i am not sure i agree that its a bad hack, Rose has a great solution but it involves typing func( (type[]){val1, val2, 0}); which feels clunky, wheras if you had #define func_short_cut(...) func((type[]){__VA_ARGS__}); then you could simply call func_short_cut(1, 2, 3, 4, 0); which gives you the same syntax as a normal variadic function with the added benefit of Rose's neat trick...whats the issue here? – chrispepper1989 Jul 24 '13 at 14:05
  • Don't you mean `while(values)`? (i.e. no valueof operator)? I take it you loop over the pointers until you reach the NULL pointer? – Gerard May 17 '14 at 12:45
  • @Gerard No, `while(*values)` is correct, look at the *value of* the last element in the argument array. – Mmmh mmh Sep 01 '14 at 11:02
  • 9
    What if you want to pass 0 as an argument? – Julian Gold Sep 10 '14 at 08:50
  • 1
    This requires your users to remember to call with a terminating 0. How is it safer? – cp.engr Feb 26 '16 at 21:26
  • @JulianGold you could still give the list length in argument instead of relying on NULL terminating the list. – cladmi Jul 28 '16 at 05:56
  • @cp.engr safer as here you have argument type written in the function signature and checking is done by the compiler. – cladmi Jul 28 '16 at 05:59
  • @RichardJ.RossIII: Perhaps you could revise your macro to actually compute the number of elements in the list, instead of using `sizeof(some_pointer)` ? – Ben Voigt Dec 04 '16 at 01:01
  • @BenVoigt a comment cannot be edited after 5 minutes, unfortunately. – Richard J. Ross III Dec 05 '16 at 17:40
  • Good idea, but I still crave my syntactic sugar! – Jiminion Jul 23 '18 at 13:13
  • Just to add - your link explaining why they can be dangerous is specific to C++ code, while this question is about C. – Dutchmanjonny Oct 23 '20 at 09:56
30

In magnificent C++11 you could use variadic templates:

template <typename... Ts>
void format_string(char *fmt, Ts ... ts) {}

template <typename... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts... ts)
{
  format_string(fmt, ts...);
}
user2023370
  • 10,488
  • 6
  • 50
  • 83
  • Don't forget that variadic templates are still not available in Visual Studio... this may well be of no concern to you of course! – Tom Swirly Sep 07 '13 at 01:01
  • 1
    If you are using Visual Studio, variadic templates can be added to Visual Studio 2012 using the November 2012 CTP. If you're using Visual Studio 2013, you will have variadic templates. – user2023370 Sep 07 '13 at 14:12
11

Though you can solve passing the formatter by storing it in local buffer first, but that needs stack and can sometime be issue to deal with. I tried following and it seems to work fine.

#include <stdarg.h>
#include <stdio.h>

void print(char const* fmt, ...)
{
    va_list arg;
    va_start(arg, fmt);
    vprintf(fmt, arg);
    va_end(arg);
}

void printFormatted(char const* fmt, va_list arg)
{
    vprintf(fmt, arg);
}

void showLog(int mdl, char const* type, ...)
{
    print("\nMDL: %d, TYPE: %s", mdl, type);

    va_list arg;
    va_start(arg, type);
    char const* fmt = va_arg(arg, char const*);
    printFormatted(fmt, arg);
    va_end(arg);
}

int main() 
{
    int x = 3, y = 6;
    showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
    showLog(1, "ERR");
}

Hope this helps.

VarunG
  • 176
  • 1
  • 4
9

You can try macro also.

#define NONE    0x00
#define DBG     0x1F
#define INFO    0x0F
#define ERR     0x07
#define EMR     0x03
#define CRIT    0x01

#define DEBUG_LEVEL ERR

#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...)  fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...)  if((DEBUG_LEVEL & X) == X) \
                                      DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)

int main()
{
    int x=10;
    DEBUG_PRINT(DBG, "i am x %d\n", x);
    return 0;
}
Jagdish
  • 1,848
  • 3
  • 22
  • 33
7

You can use inline assembly for the function call. (in this code I assume the arguments are characters).

void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
    {
        va_list argumentsToPass;
        va_start(argumentsToPass, fmt);
        char *list = new char[numOfArgs];
        for(int n = 0; n < numOfArgs; n++)
            list[n] = va_arg(argumentsToPass, char);
        va_end(argumentsToPass);
        for(int n = numOfArgs - 1; n >= 0; n--)
        {
            char next;
            next = list[n];
            __asm push next;
        }
        __asm push fmt;
        __asm call format_string;
        fprintf(stdout, fmt);
    }
Yoda
  • 119
  • 1
  • 1
2

Ross' solution cleaned-up a bit. Only works if all args are pointers. Also language implementation must support eliding of previous comma if __VA_ARGS__ is empty (both Visual Studio C++ and GCC do).

// pass number of arguments version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}


// NULL terminated array version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}
rubik
  • 8,814
  • 9
  • 58
  • 88
BSalita
  • 8,420
  • 10
  • 51
  • 68
2

Short answer

/// logs all messages below this level, level 0 turns off LOG 
#ifndef LOG_LEVEL
#define LOG_LEVEL 5  // 0:off, 1:error, 2:warning, 3: info, 4: debug, 5:verbose
#endif
#define _LOG_FORMAT_SHORT(letter, format) "[" #letter "]: " format "\n"

/// short log
#define log_s(level, format, ...)     \                                                                                  
    if (level <= LOG_LEVEL)            \                                                                                     
    printf(_LOG_FORMAT_SHORT(level, format), ##__VA_ARGS__)

usage

log_s(1, "fatal error occurred");
log_s(3, "x=%d and name=%s",2, "ali");

output

[1]: fatal error occurred
[3]: x=2 and name=ali

log with file and line number

const char* _getFileName(const char* path)
{
    size_t i = 0;
    size_t pos = 0;
    char* p = (char*)path;
    while (*p) {
        i++;
        if (*p == '/' || *p == '\\') {
            pos = i;
        }
        p++;
    }
    return path + pos;
}

#define _LOG_FORMAT(letter, format)      \                                                                        
    "[" #letter "][%s:%u] %s(): " format "\n", _getFileName(__FILE__), __LINE__, __FUNCTION__

#ifndef LOG_LEVEL
#define LOG_LEVEL 5 // 0:off, 1:error, 2:warning, 3: info, 4: debug, 5:verbose
#endif

/// long log
#define log_l(level, format, ...)     \                                                                               
    if (level <= LOG_LEVEL)            \                                                                                         
    printf(_LOG_FORMAT(level, format), ##__VA_ARGS__)

usage

log_s(1, "fatal error occurred");
log_s(3, "x=%d and name=%s",2, "ali");

output

[1][test.cpp:97] main(): fatal error occurred
[3][test.cpp:98] main(): x=2 and name=ali

custom print function

you can write custom print function and pass ... args to it and it is also possible to combine this with methods above. source from here

int print_custom(const char* format, ...)
{
    static char loc_buf[64];
    char* temp = loc_buf;
    int len;
    va_list arg;
    va_list copy;
    va_start(arg, format);
    va_copy(copy, arg);
    len = vsnprintf(NULL, 0, format, arg);
    va_end(copy);
    if (len >= sizeof(loc_buf)) {
        temp = (char*)malloc(len + 1);
        if (temp == NULL) {
            return 0;
        }
    }
    vsnprintf(temp, len + 1, format, arg);
    printf(temp); // replace with any print function you want
    va_end(arg);
    if (len >= sizeof(loc_buf)) {
        free(temp);
    }
    return len;
}
Ali80
  • 6,333
  • 2
  • 43
  • 33
0

Let's say you have a typical variadic function you've written. Because at least one argument is required before the variadic one ..., you have to always write an extra argument in usage.

Or do you?

If you wrap your variadic function in a macro, you need no preceding arg. Consider this example:

#define LOGI(...)
    ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

This is obviously far more convenient, since you needn't specify the initial argument every time.

Engineer
  • 8,529
  • 7
  • 65
  • 105
-5

I'm unsure if this works for all compilers, but it has worked so far for me.

void inner_func(int &i)
{
  va_list vars;
  va_start(vars, i);
  int j = va_arg(vars);
  va_end(vars); // Generally useless, but should be included.
}

void func(int i, ...)
{
  inner_func(i);
}

You can add the ... to inner_func() if you want, but you don't need it. It works because va_start uses the address of the given variable as the start point. In this case, we are giving it a reference to a variable in func(). So it uses that address and reads the variables after that on the stack. The inner_func() function is reading from the stack address of func(). So it only works if both functions use the same stack segment.

The va_start and va_arg macros will generally work if you give them any var as a starting point. So if you want you can pass pointers to other functions and use those too. You can make your own macros easily enough. All the macros do is typecast memory addresses. However making them work for all the compilers and calling conventions is annoying. So it's generally easier to use the ones that come with the compiler.

Jim
  • 5