不定参数宏

引入

在工程代码中,都会有较为规范的日志信息,除了表达函数是否正常执行、变量值为多少等调试信息外,还有功能模块、打印等级(debug打印、error打印)、所在文件、行数以及函数等信息。
有两种方法可实现, 如下:

  1. 定义自己的打印函数
    void print_log(int functionModule, int printLevel, char *format, ...);
    
    可以在函数内加入时间、文件名、行号以及函数名等信息。
  2. 定义各个功能模块的日志信息不定参数宏
    /*FTP为宏定义*/
    #define FTP_DEBUG(level, arg...)	        DEBUG(FTP, level, ##arg)
    #define DEBUG(module, level, format, ...)	print_log(module, level, format, ##__VA_ARGS__);
    
    虽然还是通过不定参数函数实现,但是通过给不同功能模块定义不同的打印宏,使得日志信息表达含义更加明确。

是什么

通过把宏参数列表中最后的参数写成省略号(...),使其可以接受数量可变的宏参数。

表现形式

  1. 不定参数宏__VA_ARGS__(C99)
    #define DEBUG(...)	print(__VA_ARGS__)
    
  2. GCC的复杂宏
    #define DEBUG(arg...)	print(arg)
    
    替换效果如下:
    DEBUG("hello") = print("hello")
    DEBUG("[%s][%zu][%s]", file, line, function) = print("[%s][%zu][%s]", file, line, function)
    

使用方法

头文件strarg.h中定义了一组对象、方法使得我们可以使用不定参数。

  • va_list ap:用于储存省略部分数据的对象类型
  • va_start(format, ap):使得ap指向format后的不定参数列表,即不定参数列表中的第一个参数
  • int tmp = va_arg(ap, int):将当前ap指向的值返回,并使ap指向参数列表中的下一个参数,va_arg中第二个参数类型名要与返回值类型相同
  • va_end(ap):完成清理工作,释放动态分配申请的用于存储参数的内存

其他注意事项

  1. ##的使用
    在一些情景下,如下:
    #define DEBUG(format, ...)		print(format, ##__VA_ARG__)
    
    当调用为DEBUG("hello")时,此时可变参数部分为空,那么展开为print("hello",),出现多余的,,编译器将会报错,而##的作用就是在这种情况下消除多余的,

示例

...
FTP_DEBUG(DEBUG, "this is a test.\n");
...
#define FTP_DEBUG(level, arg...)	        PRINT(FTP, level, ##arg)
#define PRINT(module, level, format, ...)	print_log_info(module, level, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__)
...
/**@brief:    打印日志信息(系统信息 + 调试信息)
 * @param[i]: 功能模块序号,int moduleIndex
 * @param[i]: 打印登记序号,int printLevelIndex
 * @param[i]: 打印日志所在文件,char *file
 * @param[i]: 打印日志所在行号,size_t line
 * @param[i]: 打印日志所在函数,char *function
 * @param[i]: 调试信息,char *format
 * @return:   无
 */
void print_log_info(int moduleIndex, int printLevelIndex, char *file, size_t line, char *function, char *format, ...)
{
	char logInfo[LOG_INFO_LENGTH] = {0};
	char debugInfo[DEBUG_INFO_LENGTH] = {0};
	struct tm currentLocalTime;
	va_list ap;
	
	if (is_value_invaild(moduleIndex, MAIN, MAX_MODULE_INDEX) \
	    || is_value_invaild(printLevelIndex, DEBUG, MAX_PRINT_LEVEL_INDEX) \
	    || NULL == file || NULL == function || NULL == format)
	{
		printf("print_log_info, input param is invalid.\n");
		return;
	}

	memset(&currentLocalTime, 0, sizeof(currentLocalTime));	

	if (get_local_time(&currentLocalTime))
	{
		printf("get_local_time failed.\n");
		return;
	}

	/*整理系统信息*/
	(void)snprintf(logInfo, sizeof(logInfo), \
		"[%02d/%02d %02d:%02d:%02d][%s][%s][%s][%zu][%s]", \
		currentLocalTime.tm_mon + 1, currentLocalTime.tm_mday, \
                currentLocalTime.tm_hour, currentLocalTime.tm_min, currentLocalTime.tm_sec, \
		g_module_atrr[moduleIndex].name, g_print_level_atrr[printLevelIndex].name, \
		file, line, function);

	/*获取调试信息*/
	va_start(ap, format);
	(void)vsnprintf(debugInfo, sizeof(debugInfo), format, ap);

	/*打印日志信息*/
	(void)strncat(logInfo, debugInfo, sizeof(debugInfo));
	printf("%s", logInfo);

	return;
}
posted @ 2021-07-26 23:55  zpchya  阅读(924)  评论(0编辑  收藏  举报