Monk
Monk

Reputation: 321

Passing va_list to other functions

I have been trying to pass variable arguments to other function in C but it is producing inconsistent result in different runtime environment as well as in different runs in same environment:

int main() 
{ 
    int result = myprintf("Something \n %d", 9); 
    return result;
}

int myprintf(const char *format, ...){
    printf("Something \n %d", 9); 
    printf("\n");
    va_list args;        
    va_start(args, format);    
    int result = printf(format,args);
    printf("\n");
    va_end(args);
    return result;
} 

And the result produced is:

WWW.FIRMCODES.COM 
 9
WWW.FIRMCODES.COM 
 438656664

I could not find the reason for "438656664".

Upvotes: 21

Views: 39613

Answers (4)

Dmitry
Dmitry

Reputation: 1156

Just a simple demonstration and worked example with "a fresh one va_list" when you need to print/output-as-string a template string like constexpr const char* example = R"(template "%s")"; .

std::string print_templ(const char* format, ...)
{
    va_list args1;
    va_start(args1, format);
    va_list args2;
    va_copy(args2, args1);

    std::vector<char> str(std::vsnprintf(nullptr, 0, format, args1) + 1);
    va_end(args1);

    const int ret = std::vsnprintf(str.data(), str.size(), format, args2);
    va_end(args2);

    return std::string(str.begin(), str.begin()+ret);
}

Upvotes: 0

Guillaume.P
Guillaume.P

Reputation: 461

For the C++ fellow also reading this. You can actually do it using pack expansion without using vprintf. This trick is quite handy when you need to wrap a method that takes the ellipsis (...) and not a va_list.

For instance:

template <class ... Args>
void foo(const char *format, Args ... args)
{
    printf(format, args...);
}

Here class ... Args is template parameter pack, Args ... args is function parameter pack, and args... is function parameter pack expansion.

Upvotes: 11

Lundin
Lundin

Reputation: 213458

Alternatively, you can simply use a wrapper macro:

#include <stdio.h>

#define myprintf(fmt, ...) ( printf("Something \n %d\n", 9), printf(fmt, __VA_ARGS__) )

int main (void) 
{ 
    int result = myprintf("Something \n %d\n", 9); 
    printf("%d\n", result);
}

Note the use of the comma operator to preserve the returned value of the right-hand printf call to the caller.

This isn't any less type safe than the (equally dangerous) stdarg.h variadic functions.

Upvotes: 2

M Oehm
M Oehm

Reputation: 29116

You cannot pass the variadic arguments to a variadic function. Instead, you must call a function that takes a va_list as argument. The standard library provides variants of printf and scanf that take a va_list; their names have the prefix v.

Your example should look like:

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

int printfln(const char *format, ...)
{
    int result;
    va_list args;

    va_start(args, format);
    result = vprintf(format, args);
    printf("\n");
    va_end(args);

    return result;
}

int main()
{
    int result = printfln("Something \n %d", 9);

    printf("(%d)\n", result);

    return 0;
}

There are some gotchas, for example when you want to call two v... function for printing to the screen and a log file: The v... function may exhaust the va_list, so you must pass in a fresh one to each call if your code should be portable.

Upvotes: 44

Related Questions