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

时间:2013-12-17 16:33:47

标签: c logging c99 wrapping

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

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

  • printf()的
  • 的snprintf()

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

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

编译器:arm-gcc -std = c99

7 个答案:

答案 0 :(得分:49)

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

  1. Variadric宏

    #define my_printf(...) printf(__VA_ARGS__)
    
  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);
    }
    
  3. 还有vsnprintfvfprintf以及stdio中您能想到的任何内容。

答案 1 :(得分:32)

由于你可以使用C99,我将它包装在variadic macro

#define TM_PRINTF(f_, ...) printf((f_), __VA_ARGS__)
#define TM_SNPRINTF(s_, sz_, f_, ...) snprintf((s_), (sz_), (f_), __VA_ARGS__)

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


编辑:

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

#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)

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

答案 2 :(得分:10)

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

#define THAT_MACRO(pargs)    printf pargs

然后使用它:

THAT_MACRO(("This is a string: %s\n", "foo"));
           ^
           |
          OMG

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

这比仅仅做得好

更好
#define THAT_MACRO printf

因为它允许你定义它:

#define THAT_MACRO(pargs)  /* nothing */

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

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

答案 3 :(得分:9)

#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)

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

用法

答案 4 :(得分:3)

#define PRINTF(...) printf(__VA_ARGS__)

这样的工作原理如下:

它定义参数化宏PRINTF接受(最多)无限参数,然后将其从PRINTF(...)预处理到printf(__VA_ARGS__)。参数化宏定义中使用__VA_ARGS__来表示给定的参数('因为你不能命名无限参数,是吗?)。

答案 5 :(得分:2)

有限的图书馆?嵌入式系统?需要尽可能多的性能?没问题!

正如对this question的回答所示,您可以使用汇编语言将不接受VA_LIST的函数包装成那些以低成本实现您自己的vprintf的函数!

虽然这会起作用,并且几乎肯定会产生你想要的性能和抽象,但我建议你可以通过切片uClibc的部分来获得一个功能更丰富的标准库。除非您绝对需要每个周期,否则这样的解决方案肯定是比使用装配更便携和整体更有用的答案。

毕竟这就是为什么存在这样的项目。

答案 6 :(得分:0)

这是@ ldav1出色答案的稍作修改的版本,它在日志之前显示时间:

#define TM_PRINTF(f_, ...)                                                                            \
{                                                                                                 \
    struct tm _tm123_;                                                                            \
    struct timeval _xxtv123_;                                                                     \
    gettimeofday(&_xxtv123_, NULL);                                                               \
    localtime_r(&_xxtv123_.tv_sec, &_tm123_);                                                     \
    printf("%2d:%2d:%2d.%d\t", _tm123_.tm_hour, _tm123_.tm_min, _tm123_.tm_sec, _xxtv123_.tv_usec); \
    printf((f_), ##__VA_ARGS__);                                                                  \
};