对于涉及多文件较大规模的程序员而言,在不同地方设置printf()
用于查看代码运行进展以及查看距离故障点最近位置是很有用的,但是手动地在系统中插入一个个printf(...)
,最终版一个个删显然太蠢了。这里介绍一个成体系的Debug函数组。
#ifdef _DEBUG
#define DEBUG(...) fprintf(stderr, __VA_ARGS__ )
#else
#define DEBUG(format,...)
#endif
这样在编译时cl /D _DEBUG...
便是启动这些printf()函数,不定义_DEBUG
便是取消Debug功能。但是这里面的__VA_ARGS__
又是些什么东西?
__VA_ARGS__
可变参数宏,使用场景:#define PRINT_ERROR(…) fprintf(stderr, VA_ARGS)
在C89年代,可变参数stdarg机理依旧只能使用在函数中,如经典的
int printf(const char* format, ...);
直到C99才允许定义可变参数宏,如上面PRINT_ERROR(...)
中的 ...
代表着这是一个可以变化的参数表,使用保留名__VA_ARGS__
把参数传给宏函数。
对于GCC采用ANSI标准而言,其支持的方式更为直观:
#define PRINT_ERROR(args...) fprintf(stderr, args)
即其支持为可变参数序列起一个名称,并在后续的宏函数中直接使用代替,而不是项上面那样一直使用__VA_ARGS__
这一系统默认的伪宏。
还有可以提供更为详细位置信息的其他宏吗?是的,看下面。
#include <stdarg.h>
#include <stdio.h>
#define DEBUG(...) printf(__VA_ARGS__)
#define XNAME(n) x##n
#define PXN(n) printf("x"#n" = %d", x##n)
//这里面古怪的"#n"中的双引号并没有用\转义,其实是因为这个部分标记并非是给程序运行段查看的
//这个"#n"是给编译器的预编译器看的,告诉它这部分内容是需要替换的宏参数,需要先进行宏参数转换操作
//比如如下这个更夸张的操作方式更是表明了""囊括的的部分会先作为宏展开的部分进行参数解读
//#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"/n", __LINE__, ##__VA_ARGS__)
int main()
{
DEBUG("当前源代码函数名:__FUNCTION__ = %s \n", __FUNCTION__ );
DEBUG("当前源代码行数:__LINE__ = %d \n", __LINE__ );
DEBUG("当前源代码文件名:__FILE__ = %s \n", __FILE__ );
DEBUG("当前编译日期〔注意和当前系统日期区别开来〕:__DATE__==%s\n", __DATE__);
DEBUG("当前编译时间〔注意和当前系统日期区别开来〕:__TIME__==%s\n", __TIME__);
DEBUG("当前系统时间戳:__TIMESTAMP__==%s\n", __TIMESTAMP__);
//DEBUG("当要求程序严格遵循ANSIC标准时该标识符被赋值为1:__STDC__==%d\n", __STDC__);
DEBUG("当用C++编译程序编译时,标识符__cplusplus就会被定义:__cplusplus==%d\n", __cplusplus);
int XNAME(1) = 12;
PXN(1);
return 0;
}
这样便可以定义出最终版的Debug的组合函数
#define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)
#define MY_ASSERT(value) if(!(value)) { PRINT_ERROR("\"%s\" failed at %s:%d %s \n",\
#value,__FILE__,__LINE__,__FUNCTION__);}
#define SHOW_LOCA() PRINT_ERROR(" current line_NO = %d \n", __LINE__ );
static FILE* g_out_file = NULL; //甚至还可以定义Log登陆文件信息,配合如下函数实现信息保存
static void InitLog( const char* file_name )
{
#ifdef BINDINGS_LOG
MY_ASSERT( file_name );
if ( g_out_file == NULL )
{
g_out_file = fopen( file_name, "w" );
}
if ( g_out_file == NULL )
{
printf( "Create %s failed\n", file_name );
}
printf( "Bindings were written into %s, please check it for details.\n", file_name );
#endif
return ;
}