C/C++ Variable Length Parameters to Realize log Output

Posted by lemming_ie on Fri, 31 May 2019 22:02:37 +0200

_ VA_ARGS__macro

Instructions

#define qWiFiDebug(format, ...) qDebug("[WiFi] "format" File:%s, Line:%d, Function:%s", ##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__);

The macro above uses qDebug to output debugging information, printf in non-Qt programs, syslog in daemons and so on. The trick is actually these macros ____________________________
1) The _VA_ARGS_ is a macro with variable parameters. Few people know about this macro. This macro with variable parameters is added to the new C99 specification. At present, it seems that only gcc support (the compiler of VC6.0 does not support it). The effect of adding # before macro is that when the number of variable parameters is 0, the # here is superfluous to the front "," remove the effect, or else compile errors, you can try.
2) The _FILE__macro will be replaced by the current source file name when precompiled
3) The _LINE_ macro will be replaced by the current line number when precompiled
4) The _FUNCTION_ macro is replaced by the current function name at precompiled time

sample code:

 //Debugging information is exported to the following files
#define DEBUG_FILE "/tmp/debugmsg"
//Buffer Length of Debugging Information
#define DEBUG_BUFFER_MAX 4096
//Export debugging information to files
#define printDebugMsg(moduleName, format, ...) {\
    char buffer[DEBUG_BUFFER_MAX+1]={0};\
    snprintf( buffer, DEBUG_BUFFER_MAX \
            , "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );\
    FILE* fd = fopen(DEBUG_FILE, "a");\
    if ( fd != NULL ) {\
        fwrite( buffer, strlen(buffer), 1, fd );\
        fflush( fd );\
        fclose( fd );\
    }\
}  With these macros, especially with__VA_ARGS__ ,The output of debugging information becomes much more flexible.

vsnprintf and snprintf

Instructions

vsnprintf and snprintf are members of the printf family function in C language. The list of related functions is as follows:

#include <stdio.h>
int printf(const char *format, ...); //Output to standard output
int fprintf(FILE *stream, const char *format, ...); //output to a file
int sprintf(char *str, const char *format, ...); //Output to string str
int snprintf(char *str, size_t size, const char *format, ...); //Output to string str by size

The following functions have the same functions as the one-to-one correspondence above, except that when a function is called, the above functions are _________. The corresponding variables are replaced by va_list calls. The ap is dynamically acquired through the va_start() macro before a function call.

#include <stdarg.h>
int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

Variable parameter list va_list macro description:

void va_start(va_list ap, last); 
void va_end(va_list ap);

va_start and va_end are called in pairs.
Initially, va_start is called to get the address of each output variable.
At the end, va_end is called to release the corresponding resources.
For example, snprintf() is implemented through vsnprintf(): (for more details, refer to the Linux man va_start manual)

sample code:

#include <stdio.h>
#include <stdarg.h>
int my_snprintf(char *s, int size, const char *fmt, ...) //The custom function is the same as the snprintf() function provided by the system.
{
    va_list ap;
    int n=0;
    va_start(ap, fmt); //Get a list of variable parameters
    n=vsnprintf (s, size, fmt, ap); //Write string s
    va_end(ap); //Release resources
    return n; //Returns the number of characters written
}
int main() {
    char str[1024];
    my_snprintf( str, sizeof(str), "%d,%d,%d,%d",5,6,7,8);
    printf("%s\n",str);
    return 0;
}

Relevant test source code

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

#define LOG_WRITE_OUT(level, fmt, ...)              \
    do{                                                 \
        writeLog0(level, fmt, ##__VA_ARGS__);           \
    }while(0);

#define writeLog2(level, format, ...)                           \
    do{                                                         \
        printf("debug:[%s %d]\n", __FUNCTION__, __LINE__);      \
        char str2[512 + 1];                                     \
        snprintf(str2, 512, format, ##__VA_ARGS__);             \
        printf("[Msg level:%d]writeLog2 %s\n", level, str2);    \
    }while(0);

static void writeLog1(int level, const char *format, va_list vl)
{
    printf("debug:[%s %d]\n", __FUNCTION__, __LINE__);

    char str[512 + 1] = "";

    int size = vsnprintf(str, 512, format, vl);
    if (size <= 0) {
        return;
    }
    writeLog2(level, "writeLog1 %s", str); //Param2 places strings and formatted output symbols in one string.
}

static void writeLog0(int level, const char *format, ...)
{
    printf("debug:[%s %d]\n", __FUNCTION__, __LINE__);

    char str[512 + 1] = "";

    va_list arg_ptr;
    va_start(arg_ptr, format);
    writeLog1(level, format, arg_ptr);
    va_end(arg_ptr);
}

int main(int argc, char** argv)
{
    LOG_WRITE_OUT(1, "test: %s %d", __FUNCTION__, __LINE__);
    LOG_WRITE_OUT(1, "test end");
}

Output results:

debug:[writeLog0 33]
debug:[writeLog1 20]
debug:[writeLog1 28]
[Msg level:1]writeLog2 writeLog1 test: main 46
debug:[writeLog0 33]
debug:[writeLog1 20]
debug:[writeLog1 28]
[Msg level:1]writeLog2 writeLog1 test end

Reference material

Topics: Qt Linux C