1

I have used variadic functions to wrap printf (or vprintf).

The following code works except for making mistake on the first variadic argument to warning_printf. Also, placing the string directly will change the ASCII character but it does not fix it as the message is still random.

What it prints is

[Warning]
          ®¯$ address: 0x87afae8a

Instead of

[Warning] Failed to initialize setting address: 0x87afae8a

Where the word Warning is colored properly (anyway it does not matter). But the msg_warn seems not being passed correctly. I tested adding more variables to this function. They all work fine except for only the first variadic argument msg_warn.

What is wrong with my code?

void colorful_printf( const char* header, const char* color, const char* fmt, ... )
{
    printf("[%s%s%s] ", color, header, RESET_ANSI_COLOR);
    va_list args;
    va_start( args, fmt );
    vprintf(fmt, args);
    va_end( args );
}


void warning_printf( const char* fmt, ... )
{
    va_list args;
    va_start( args, fmt );
    colorful_printf("Warning", WARNING_COLOR, fmt, args);
    va_end( args );
}


char msg_warn[] = "Failed to initialize setting";
warning_printf( "%s address: 0x%2x", msg_warn, address );

Online compiler: link

alex
  • 257
  • 2
  • 7
  • 1
    You need a function that takes a `va_list` instead of `...`. – Jonathan Leffler Apr 12 '19 at 00:36
  • @JonathanLeffler, by copy or by reference? – alex Apr 12 '19 at 00:38
  • By copy. I think [Repeated use of a variadic function argument doesn't work](https://stackoverflow.com/questions/9309246/repeated-use-of-a-variadic-function-argument-doesnt-work) has relevant material, but I also think I've written a more relevant answer — I just can't find it. Sometimes, it is easier to write the answer over than to find the right Q&A. – Jonathan Leffler Apr 12 '19 at 00:40
  • Th question [Passing variable arguments to another function that accepts a variable argument list](https://stackoverflow.com/questions/3530771/) is tagged for C++, but the answer applies equally to C. It covers the same territory as my answer below — you create the function that takes the `va_list` instead of `...` and the `...` functions create a `va_list` and then call the function that takes the `va_list` to do the real work. We will need to come to a consensus on whether it is OK as a duplicate despite the mismatch between C++ and C, or contemplate retagging it as C++ _and_ C (ouch!). – Jonathan Leffler Apr 12 '19 at 00:59

1 Answers1

2

You need to pass the va_list to a function that expects them — an analogue to the v*printf() functions.

void colorful_vprintf(const char* header, const char* color, const char* fmt, va_list args)
{
    printf("[%s%s%s] ", color, header, RESET_ANSI_COLOR);
    vprintf(fmt, args);
}

void colorful_printf(const char* header, const char* color, const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    colorful_vprintf(header, color, fmt, args);
    va_end(args);
}

void warning_vprintf(const char* fmt, va_list args)
{
    colorful_vprintf("Warning", WARNING_COLOR, fmt, args);
}

void warning_printf(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    warning_vprintf(fmt, args);
    va_end(args);
}

The *_vprintf functions do the real work; the functions that have ellipsis simply get the va_list for the arguments and pass them to the *_vprintf() functions. This is a general pattern for interfaces to (wrappers around) the printf() family of functions.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278