现在的位置: 首页 > 综合 > 正文

C宏的一个技巧:可变参数

2013年05月16日 ⁄ 综合 ⁄ 共 2404字 ⁄ 字号 评论关闭

 

前天晚上,和一个朋友天南地北地聊一些技术问题。无意中给我一个网址http://www.nongnu.org。无意中我发现了GNU Coding Standards(现在的我对一些专业名词非常敏感),于是不由自主地点击了进去。结果让我大开眼界增长了见识。这些单表一处:错误信息格式及一些宏技巧。

GNU Coding StandardsFormatting Error Messages提到了错误信息的格式,大约如下:

source-file-name:lineno: message

联想到我以前的出错信息都没有添加源代码文件名称以及行号,只是一个简单的宏定义,如下:

#define unix_error_exit(error)                  \
    do{                                         \
        fprintf(stderr, "%s: %s/n",             \
                error, strerror(errno));        \
        exit(1);                                \
    } while(0)

于是再次修改,添加了__FILE__和__LINE__的信息,如下:

#define unix_error_exit(error)                  \
    do{                                         \
        fprintf(stderr, "%s: %d: %s: %s/n",       \
                __FILE__, __LINE__, error, strerror(errno)); \
        exit(1);                                \
    } while(0)

这个宏的打印信息效果如下:

main.c: 31: Open file error: No such file or directory.

本来想将函数名称也添加上去,但似乎有点长了,就不添加了。

关于错误信息处理,我还有另外的真正的函数版本。这是从APUE最后附录的例子的基础上稍作修改而成的。
一个例子如下:

void unix_error_exit(const char *fmt, ...);

前缀为unix,这是表示这个处理系统调用的出错信息处理(GNU Coding Standards也强调了系统调用出错信息处理的重要性)。同样,上面的函数是不能打印源文件名称和行号的,现在需要添加上去,变成这个样子:

void unix_error_exit(const char *file_name, int line, const char *fmt, ...);

在代码中很方便将参数file_name和line传递到缓冲区中。但我们需要的__FILE__和__LINE__,如果直接放到这个函数中,将不能实现我们的目标——因为我们不是打印这个函数的文件名和行号。
因此,我们必须在真正的调用处才能使用这两个宏。于是,很自然想到了宏定义:

#define x__unix_error_exit(fmt) unix_error_exit(__FILE__, __LINE__, fmt)

但是,这个宏定义只能带一个参数,像printf函数那种格式就不成功。——当然,对于简单的提示信息,这已经足够了。可以这样使用:

x_unix_error_exit("Open file error");

但是,能不能将这个宏也做成像printf那样,自由地带参数呢?APUE上的例子本来就是可以带多个参数,即使这里用不着,但是以后可能会有用得到的场合呢?这个问题我想了好久,也没什么方法。

今天在讨论上乱看帖子,不小心看到了一篇关于C宏技巧的文章:http://www.javaeye.com/topic/814136。里面有一个宏定义:

#define gnu_eprintf(format, args...)       fprintf(stderr, format, ##args) 

对我帮助非常大,原来是可以这样的!于是我马上将我的代码作了修改,如下:

#define x_unix_error_exit(fmt, args...) unix_error_exit(__FILE__, __LINE__, fmt, ##args)

当然,也可以修改成为C99风格的:

#define x_unix_error_exit(fmt, ...) unix_error_exit(__FILE__, __LINE__, fmt, __VA_ARGS__)

测试例子:

x_unix_error_exit("%d %s Open file error", 88, "hello world");

效果如下:

main.c: 31: 88 hello world Open file error: No such file or directory.

当然,这里仅是测试而已,不过已经达到我们的目的。而这技巧,对于以后的日志函数将大有作用。

前一天还在为某一问题而烦恼,但今天不小心就找到了解决之法,或者这是冥冥中自有天意吧。我始终坚信我一直以来学的东西都会派上用场的,它们也相互有联系(最鲜明的例子就是我不将C语言只看成一门语言,它跟很多学科都有联系)。生活中很多东西也有联系,从多个角度分析问题,有时会对社会看得更深入一些,心态看开了,生活也自然不会忧郁。当然,每个人对幸福的定义是不同的。

PS:

1、本文涉及代码依然还在修改当中,错误难免存在,就不便公开,如果实在需要,可在文后留言给我。

2、google得来的一篇文章:

C Macro Tips and Tricks:
http://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html

【上篇】
【下篇】

抱歉!评论已关闭.