如何将printf()包装到函数或宏中?

Vor*_*rac 28 c logging c99 word-wrap

这听起来有点像面试问题,但实际上是一个实际问题.

我正在使用嵌入式平台,并且仅提供这​​些功能的等价物:

  • 的printf()
  • 的snprintf()

此外,printf()实现(和签名)很可能在不久的将来发生变化,因此对它的调用必须驻留在一个单独的模块中,以便以后易于迁移.

鉴于这些,我可以在一些函数或宏中包装日志记录调用吗?目标是我的源代码THAT_MACRO("Number of bunnies: %d", numBunnies);在一千个地方调用,但是只能在一个地方看到对上述函数的调用.

编译器:arm-gcc -std = c99

Ser*_* L. 54

有两种方法可以做到这一点:

  1. Variadric宏

    #define my_printf(...) printf(__VA_ARGS__)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 转发的功能 va_args

    #include <stdarg.h>
    #include <stdio.h>
    
    void my_printf(const char *fmt, ...) {
        va_list args;
        va_start(args, fmt);
        vprintf(fmt, args);
        va_end(args);
    }
    
    Run Code Online (Sandbox Code Playgroud)

还有vsnprintf,vfprintf无论你能想到什么stdio.


lda*_*v1s 36

既然你可以使用C99,我将它包装在一个可变的宏中:

#define TM_PRINTF(f_, ...) printf((f_), __VA_ARGS__)
#define TM_SNPRINTF(s_, sz_, f_, ...) snprintf((s_), (sz_), (f_), __VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)

因为你没有说你有vprintf或类似的东西.如果你确实有这样的东西,你可以将它包装在谢尔盖L在他的答案中提供的功能中.


编辑:

上述TM_PRINTF不适用于空的VA_ARGS列表.至少在GCC中可以写:

#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)

如果__VA_ARGS__为空,则两个##符号删除它们前面的多余逗号.

  • `TM_PRINTF("Starting ei-oh!");` 产生 `错误:')' 标记之前的预期表达式`。如果没有格式字符串参数,它就可以工作。可变参数的数量是否需要非零? (3认同)

unw*_*ind 11

如果您可以将呼叫包含在两个括号中,您可以这样做:

#define THAT_MACRO(pargs)    printf pargs
Run Code Online (Sandbox Code Playgroud)

然后使用它:

THAT_MACRO(("This is a string: %s\n", "foo"));
           ^
           |
          OMG
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为从预处理器的角度来看,整个参数列表成为一个宏参数,用括号替换.

这比仅仅做得好

#define THAT_MACRO printf
Run Code Online (Sandbox Code Playgroud)

因为它允许你定义它:

#define THAT_MACRO(pargs)  /* nothing */
Run Code Online (Sandbox Code Playgroud)

这将"吞噬"宏参数,它们永远不会成为编译代码的一部分.

更新当然在C99中这种技术已经过时,只需使用可变参数宏并感到高兴.

  • 这种技术的一个重要副作用:所有对`THAT_MACRO`的调用都需要双括号,即使是单参数调用,例如`THAT_MACRO(("Foo Bar"))`。——爱,几年后的一个可怜的C89程序员。 (2认同)

Fel*_*tti 9

#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)

##令牌将启用使用率TM_PRINTF("aaa");

  • 请注意,这是编译器扩展,而不是C99标准.基本上,编译器编写者,他们是聪明人,在标准中认识到这种疏忽,并找到了解决方法.需要注意的是,它不能保证在每个符合C99的编译器上工作,并且一些编译器可能会使用不同的语法来实现相同的功能. (3认同)