C/C++中自定义信息输出——printf与宏的配合使用

在C/C++中,提起“宏”多少有些皱眉,至少我在入门C++时旁人好心提醒:尽可能地使用typedef与const常量定义来替代“宏”的使用:

1. 类型宏定义

  #define HANLE void*
	//可以替换为: 
	typedef void *HANLE;

2. 常量定义

  #define MAX_LIMIT 4096
	//可以替换为:
	const int s_MAX_LIMIT 4096;

因为宏定义是在预编译阶段,对内容进行直接替换,因此无法提供安全的类型检查等功能。

但我本次要说的是,宏的一个很炫的应用:

配合printf函数能够方便地自定义输出格式和内容

举例来说吧,就是用户在程序中调用:

USER_PRINT( "i should checkout: v_a value is %d.\n", value_a );

能够输出类似信息

[2013-10-01 15:18:28][Fox Test][PID:2486]i should checkout: v_a value is 10.

当然,如果你用过linux下的syslog,你会发现这条输出信息算是“高仿”了。syslog可是linux下调试/监控的优秀组件,简单易用不说,功能也比较完备!

扯远了,本次的目标就是如何在用户简单输出一条信息的时候,我们能够同时加上各种附加信息,包括时间/用户/PID等参数,方便我们的调试/监控。


1. 首先,提供一个有意思的宏,该宏也是该功能的核心

#define USER_PRINT_BASE( format, args... ) 	printf( format, ##args )

是不是看到args...有点儿蒙,以及##args又是闹哪样?

说明一下:

i: args...是一个参数,是C/C++中定义变参函数(参数不固定)的一种格式。至于举个变参函数的例子,printf/scanf什么的就是随处可见的例子。

ii: ##args是宏的一个特殊语法应用(和普通的##宏应用不同[TODO]),使得宏展开时可以去掉可能存在的多余的','。

举例:

当没有##时,USER_PRINT_BASE( "something debug" )在宏展开时会成为printf( "something debug", )多了一个逗号!

当然,如果你调用USER_PRINT_BASE( "something debug--%d", 10 )的时候,即使没有'##'也不会出错。

iii: 如果你的编译器支持C99规范,那么宏也可以改写为:

#define USER_PRINT_BASE( format, ... ) 		printf( format, ##__VA_ARGS__ )


嗯好吧,其实只是变参的书写格式发生了点变化。

注:ii与iii详细的可以参考“C/C++可变参数,“## __VA_ARGS__”宏的介绍和使用”,里面有很详细的介绍。

2. 之后,在核心功能之上,我们实现功能的叠加吧:

#include <time.h>
#define USER_PRINT_0( format, args... ) \
{ \
        time_t timep; \
        time( &timep ); \
        struct tm *pTM = gmtime( &timep ); \
        USER_PRINT_BASE( "[%4d-%02d-%02d %02d:%02d:%02d]"format, \
                pTM->tm_year+1900, pTM->tm_mon, pTM->tm_mday, pTM->tm_hour+8, pTM->tm_min, pTM->tm_sec, ##args ) ; \
}

这是加入时间信息。好吧,我承认时间的处理上我土了,但是asctime函数返回时默认有个换行符,待之后在订正吧。[TODO]。

需要注意:

i: 为什么这段前后要加大括号咧?利用作用域及时销毁time_t变量和pTM变量,否则连续调用USE_PRINT_0会出现重定义错误哦~

ii: USE_PRINT_0中的是(args...),在替换的USE_PRINT_BASE中还是需要(##args)哦~

iii: 附加的格式化内容也是可以放在format之后的哦,当然,注意调节变参的顺序就好。

3. 接下来,就像搭积木一样来就好了:

#include <unistd.h>
#define MAX_LIMIT 4096
const char useName[4096] = "Fox Test";
#define USER_PRINT_1( format, args... )		USER_PRINT_0( "[%s]"format, useName, ##args )
#define USER_PRINT_2( format, args... )		USER_PRINT_1( "[PID:%d]"format, getpid(), ##args )	//getpid() 获取当前进程ID
#define USER_PRINT( format, args...)		USER_PRINT_2( format, ##args )

读者可能感叹,竟然可以这样往上加参数么?我的建议是,自己玩儿一下,这的很有趣。

用户调用

直接调用类似printf一样调用USER_PRINT即可默认地输出多种调试信息,十分方便!

完整代码:

#include <stdio.h>

#include <time.h>

#include <unistd.h>

#define MAX_LIMIT 4096
const char useName[4096] = "Fox Test";

#define USER_PRINT_BASE( format, args... ) 	printf( format, ##args )

#define USER_PRINT_0( format, args... ) \
{ \
        time_t timep; \
        time( &timep ); \
        struct tm *pTM = gmtime( &timep ); \
        USER_PRINT_BASE( "[%4d-%02d-%02d %02d:%02d:%02d]"format, \
                pTM->tm_year+1900, pTM->tm_mon, pTM->tm_mday, pTM->tm_hour+8, pTM->tm_min, pTM->tm_sec, ##args ) ; \
}
#define USER_PRINT_1( format, args... )		USER_PRINT_0( "[%s]"format, useName, ##args )
#define USER_PRINT_2( format, args... )		USER_PRINT_1( "[PID:%d]"format, getpid(), ##args ) //getpid() 获取当前进程ID
#define USER_PRINT( format, args...)		USER_PRINT_2( format, ##args )

int main()
{
	USER_PRINT( "Test\n" );
	USER_PRINT( "Test %d\n", 10 );
	return 0;
}



参考资料:1. C/C++可变参数,“## __VA_ARGS__”宏的介绍和使用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值