简体   繁体   中英

chaining variadic function calls

A printf function-like call from a function with variable number of arguments is easy enough to make - just use a v -version of those functions ( vprintf , vsprintf , CString::FormatV , etc). But what if I'm chaining the calls up? Here's the simple code:

#include <stdarg.h>
#include <iostream>
void direct(const char * _fmt, bool _extra, ...){
    va_list args;
    va_start(args, _extra);

    char ca[200];
    vsprintf(ca, _fmt, args);
    std::cout << ca << std::endl;

    va_end(args);
}

void chained(const char * _fmt, ...){
    va_list args;
    va_start(args, _fmt);
    direct(_fmt, false, args);
    va_end(args);
}

int main(){
    direct("direct works just fine: %d", false, 1);
    chained("indirect produces garbage: %d", 1);
    return 0;
}

A sample output is as follows:

direct works just fine: 1
indirect produces garbage: 1951661256

I feel that I'm missing something obvious but can't figure it out thus far. Please help me fix it so that whether I call direct or chained the code works properly.

Flagged the question as C/C++ but I prefer a C++ answer (if there's a difference)

I feel that I'm missing something obvious but can't figure it out thus far

You did. And it's something you actually started with: "just use a v-version of those functions" . The reason those functions got a v-version was to allow, as you called it, chaining them. So if you want to support it for your own printf-like function, make sure to follow the same practice:

void direct_v(const char * _fmt, bool _extra, va_list args){
    char ca[200];
    vsprintf(ca, _fmt, args);
    std::cout << ca << std::endl;
}

void direct(const char * _fmt, bool _extra...){
    va_list args;
    va_start(args, _extra);
    direct_v(_fmt, _extra, args);
    va_end(args);
}

void chained(const char * _fmt...){
    va_list args;
    va_start(args, _fmt);
    direct_v(_fmt, false, args);
    va_end(args);
}

A nice emerging property of splitting direct like this is that you get better separation of concerns. The wrapper does the va_list related bit, and the v-function only cares about what needs to be done with the list, which is what allows for reuse here.


Pre-edit note: BTW, if C compatibility is indeed a concern, the function prototypes need a comma to separate the last argument from the ellipsis. The syntax you used is C++ only.

You cannot chain calls to C-style variadic functions. The only way is to pass va_list as an argument. This is exactly the reason why the v* family of functions is needed.

So you write your v*-like functions in terms of va_list , and then wrap each one in an ellipsis-based variadic function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM