tangptr@126.com 发表于 2024-2-6 15:01:06

【C】用可变参宏增强调试输出


# 前言
在大型项目中使用`printf`大法进行调试的时候,如果能同时输出所在行,那么分析log就会简单得多。

# 自制`printf`
首先自制一个`printf`,要加上行号和文件名。
```C
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

int vprintf2(const char* src_fn,const int src_ln,const char* format,va_list arg_list)
{
    char buff;    // 记得调整缓冲区大小!
    int a=snprintf(buff,sizeof(buff),"[%s@%d] ",src_fn,src_ln);
    int b=vsnprintf(&buff,sizeof(buff)-a,format,arg_list);
    // 可以把fwrite替换成别的输出,比如OutputDebugStringA
    fwrite(buff,sizeof(char),a+b,stdout);
    return a+b;
}

int printf2(const char* src_fn,const int src_ln,const char* format,...)
{
    int ret;
    va_list arg_list;
    va_start(arg_list,format);
    ret=vprintf2(src_fn,src_ln,format,arg_list);
    va_end(arg_list);
    return ret;
}
```
调用`printf2`就这么写:
```C
printf2(__FILE__,__LINE__,"Hello World!");
```
但是每次调用`printf2`都要加上`__FILE__`和`__LINE__`就很蛋疼,而且我们还不能在`vprintf2`的实现中直接放这两个宏,否则log中的文件名和行号永远是vprintf2这个函数的位置。

# 宏包装
不过我们可以用宏来包装`printf2`:
```C
#define printf3(fmt,...)    printf2(__FILE__,__LINE__,fmt,__VA_ARGS__)
```
这个宏定义在MSVC是没问题的,但是在GCC和clang里用是有坑的。
举个例子,`printf3("Hello");`在宏展开时会变成`printf2(__FILE__,__LINE__,"Hello",);`,注意括号前多出了一个逗号。这是因为我们没有使用额外的参数所导致的。**而MSVC允许在括号前多出一个逗号**,因此仍然能编译通过。
要让GCC和clang对这种情况进行特殊处理,需要改变`__VA_ARGS__`的使用方式:
```C
#define printf3(fmt,...)    printf2(__FILE__,__LINE__,fmt,##__VA_ARGS__)
```
虽然(https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html),但是**MSVC也支持这个语法**。因此直接用上述的套路包装`printf`即可。

# 安全隐患
在对外发布的时候,程序输出的日志是不可以泄漏源文件的信息的,因此需要用条件编译把`printf3`包装起来,使得对外发布的版本里不包含源文件信息。
```C
#if defined(_DEBUG)
#define printf3(fmt,...)    printf2(__FILE__,__LINE__,fmt,##__VA_ARGS__)
#else
#define printf3(fmt,...)    printf(fmt,##__VA_ARGS__)
#endif
```

AyalaRs 发表于 2024-2-15 02:04:02

如果只是调试输出,早期版本可以这么做,毕竟不支持可变参数宏
#define tr printf("[%s@%d]:",__FILE__ , __LINE__);printf

YY菌 发表于 2024-2-6 18:36:00

Release模式可以考虑直接 #define printf3(fmt, ...) 0 //什么都不输出,直接返回0(因为printf函数返回值是int类型)。

AyalaRs 发表于 2024-2-15 01:49:20

可惜早期c标准不支持可变参宏

QZhi 发表于 2024-2-19 13:55:35

刚看到https://www.zhihu.com/question/552041042
说 C23 支持这样写:
#define debug(format, …) fprintf(stderr, format __VA_OPT__(,) __VA_ARGS__)
页: [1]
查看完整版本: 【C】用可变参宏增强调试输出