发件人: Phil Howard
主题: Re: 如何为宏提供可变数量的参数
新闻组:comp.lang.c
Message-ID: <Vv0D2.339$v8.10430@newsfeed.slurp.net>
日期: 1999年3月3日 01:46:29 GMT

这里有另一种不在 FAQ 中的方法。它确实依赖于该但没有函数存在于您的库中,并且编译器能够优化掉无法执行的表达式。大多数现代库和编译器都具备此功能。即使缺少优化,该技巧仍然有效,尽管可执行文件会包含一些死代码。

我首先根据符号“DEBUG”的状态定义符号“debug”,如下所示:(这可以放在您的 .h 文件中)

#ifdef DEBUG
#define debug                   _debug_call
#else
#define debug                   1?0:
#endif

然后我定义一个名为“_debug_call()”的函数,该函数接受一个或多个固定参数,后跟可变数量的参数。例如:

        int _debug_call( int msglev, const char *msgfmt, ... )

该函数需要包含 stdarg.h 并定义一个变量

        va_list arg_list;

然后,在执行诸如决定是否打印消息之类的代码之后(例如,我有不同的调试级别),我将执行:

        va_start( arg_list, msgfmt );
        vfprintf( stderr, msgfmt , arg_list );
        putc( '\n', stderr );

现在,如果未定义符号 DEBUG,则定义符号 debug 为字符串“1?0:” 。当预处理器看到类似以下内容时:

        debug( 1, "begin %s", argv[0] );
它会将其转换为:
        1?0:(1,"begin %s",argv[0]);
这现在是一个?:求值左侧的表达式:但不是右侧。右侧只是一个带括号的表达式,如果对其进行求值,它将丢弃除最后一个之外的所有逗号分隔的表达式,只留下最后一个。但是?:导致其不被求值,并且一个好的编译器会识别出,由于 1 是一个常量,右侧的:永远不会被求值,甚至不会对其进行编译。它因此成为一个独立的(free standing)值。如果您选择分配debug()到类似
        result = debug( 1, "begin %s", argv[0] );
然后您将得到
        result = 1?0:(1,"begin %s",argv[0]);
其效果是
        result = 0;
现在,如果DEBUG被定义,那么它将变为
        _debug_call(1,"begin %s",argv[0]);
这样一来,它就被编译为一个函数调用。您确实需要将“DEBUG”的定义放在决定如何定义“debug”的预处理器代码之前。我通过将决定如何定义“debug”的部分放在一个名为 debug.h 的头文件中来实现这一点,并且我只需在包含 debug.h 之前定义或不定义符号“DEBUG”。

上面我提供了一个简单的版本。我实际的调试包要复杂一些,具有管理调试级别、输出文件以及一个名为trace的独立命名复制(与上面一样,但有一个新的名称,我可以使用它来表示一组不同的概念,以及一个单独的消息级别状态)。

如果任何人想将上述技巧包含在 FAQ 中或发布它,请随时这样做。

--
 --    *-----------------------------*      Phil Howard KA9WGN       *    --
  --   | Inturnet, Inc.              | Director of Internet Services |   --
   --  | Business Internet Solutions |       eng at intur.net        |  --
    -- *-----------------------------*      phil at intur.net        * --