【问题标题】:Variadic macros alternative in ANSI CANSI C 中的可变参数宏替代
【发布时间】:2014-12-26 23:38:24
【问题描述】:

我知道 C99 中已添加可变参数宏(并通过 GNU 扩展)。我一直想知道在 ANSI C 中是否有一个不错的替代方案。

我想出了这样的东西,但还是有点尴尬:

void log_info(const char *file, int line, const char *fmt, ...)
{
#ifdef DEBUG
    va_list ap;
    char newfmt[1024] = { 0 };
    va_start(ap, fmt);
    sprintf(newfmt, "[INFO] (%s:%d): %s\n", file, line, fmt);
    vfprintf(stderr, newfmt, ap);
    va_end(ap);
#endif
}

这样可以这样调用:

log_info(__FILE__, __LINE__, "info message: %s %d", "helloworld", 12);

这种方法没有错,但是我想知道是否有更好的方法?例如。无需每次都指定文件/行。

如有任何反馈,我将不胜感激。 :)

编辑:这里的 ANSI C 是指 C89。

编辑:下面的答案很好,但我相信鉴于它需要运行打印命令,它可能会带来一些线程安全问题。另一种选择可能是使用定义来最小化打字(也很丑陋):

#define __FL__ __FILE__, __LINE__

然后运行如下命令:

log_info(__FL__, "info message: %s %d", "helloworld", 12);

【问题讨论】:

  • 在什么情况下,您可能会使用不支持可变参数宏的编译器编写 C 语言?
  • @Alexis King:感谢您的回复。例如,当 C89 是公司标准或特定项目的要求时?我知道即使使用 -ansi 标志,大多数编译器仍然会编译代码,我的问题不是我为什么要关心,而是如果我受到这样的限制但仍然需要该功能,我会/应该做什么。我必须承认,这更像是一个好奇的问题。
  • 请使用snprintf(newfmt, sizeof(newfmt), "…", …) 防止缓冲区溢出。虽然(重新考虑),我想如果你没有 C99,你可能也没有snprintf()。这并不完全正确——MSVC 主要是 C89,但库包括 snprintf()。至少,尽可能使用snprintf()
  • "ANSI C" 用词不当。它通常指的是1989 ANSI C标准所描述的语言(与1990 ISO C标准所描述的语言相同),但实际上ANSI已经采用了ISO C标准的每个版本(1990、1999、2011)出版后不久。
  • 你应该看看C #define macro for debug printing的讨论

标签: c c89 variadic-macros


【解决方案1】:

有点难看,但括号中的逗号分隔表达式序列可以视为单个参数。

一个例子:

#include <stdio.h>

#define LOG(args) (printf("LOG: %s:%d ", __FILE__, __LINE__), printf args)

int main(void) {
    LOG(("Hello, world\n"));
    int n = 42;
    LOG(("n = %d\n", n));
    return 0;
}

输出:

LOG: c.c:6 Hello, world
LOG: c.c:8 n = 42

请注意,这需要在调用中添加一组额外的括号。

【讨论】:

  • 谢谢。这很有趣。
  • 这种方法的唯一警告是您必须运行 printf(或任何其他打印功能)两次,我认为这可能会给多线程应用程序带来一些问题。
【解决方案2】:

这是一个更丑陋的选项,但使用了对 printf 的一次调用:

#define PROMPT "[INFO] (%s:%d): "
#define FL __FILE__, LINE__

#define DEBUG0(fmt) printf(PROMPT fmt, FL);
#define DEBUG1(fmt, a1) printf(PROMPT fmt, FL, a1);
#define DEBUG2(fmt, a1, a2) printf(PROMPT fmt, FL, a1, a2);
#define DEBUG3(fmt, a1, a2, a3) printf(PROMPT fmt, FL, a1, a2, a3);

以此类推,直到您调用宏时使用的参数数量最多。

【讨论】:

  • 谢谢。我一开始觉得这很尴尬,但我越想越喜欢这个解决方案。唯一不好的一面是需要创建很多定义。
  • @Alex 是的,设置起来很糟糕,但是一旦它全部隐藏在标题中,它在你的其他源文件中就不是那么糟糕了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-28
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
  • 2010-12-24
  • 1970-01-01
相关资源
最近更新 更多