将宏转换为函数调用

时间:2015-01-27 19:45:01

标签: c

我创建了一个宏,它将打印所有打印的时间戳。

void timestamp()
{
    struct timeval tv;
    gettimeofday(&tv,NULL);
    printf("%d",tv.tv_sec );
}

#define printf_all(format, ...) {                           \
    static const char format_string[] = format; \
    printf(format_string, ##__VA_ARGS__);                  \
    timestamp(); \
}

int main()
{
    printf_all("%d\n",10);
    return 0;
}

我想将此宏转换为函数。但是在传递论据时我遇到了问题。

void printf_timestamp(static const char format_string[]) {                           
    static const char format_string[] = format; 
    printf(format_string, ##__VA_ARGS__);                  
    timestamp(); 
}

3 个答案:

答案 0 :(得分:5)

您可以使用vprintf

int printf_timestamp(const char* fmt, ...) {
    va_list args;
    int result;

    va_start(args, fmt);
    result = vprintf(fmt, args);
    va_end(args);
    timestamp();

    return result;
}

答案 1 :(得分:0)

这通常使用vprintf()函数来完成,正如orlp所解释的那样。但是,单独使用一种格式无法进行类型检查:通常,您的编译器会在它看到的每个printf()调用中解释格式字符串文字,以确定相应的参数是否具有正确的类型。

有些编译器允许您添加对此类型检查的支持,以下是__attribute__(())所需的gcc

//within header
void printfTimestamp(const char* format, ...)
    __attribute__((format(printf, 1, 2)));

//within implementation file
void printfTimestamp(const char* format, ...) {
    va_list args;
    va_start(args, format);
    int result = vfprintf(stderr, format, args);
    va_end(args);
    timestamp();
    return result;
}

属性的参数是带有格式字符串(1)的参数号和第一个变量参数位置(2)。因此,如果要向函数添加其他参数,则应将其声明为:

void myPimpedPrintf(int foo, const char* format, ...)
    __attribute__((format(printf, 2, 3)));

答案 2 :(得分:0)

如果只是添加一个时间戳,我同意orlp提出的vprintf()是要走的路。但是,我认为有一个有利于宏的有效论据,那就是使用其他编译器内置宏,如__LINE____FILE____func__,我个人非常喜欢对记录有用。

我通常会使用gcc以及其他任何可以吞咽逗号的内容。

#define log_debug(M, ...) do{ \
  struct timespec _ts; \
  double _ts_sec; \
  clock_gettime(CLOCK_MONOTONIC, &_ts);\
  _ts_sec = _ts.tv_nsec * 1e-9 + _ts.tv_sec; \
  fprintf(stderr, "%.3f [DEBUG] (%s:%d) " M,  _ts_sec, \
      __func__, __LINE__, ##__VA_ARGS__); \
}while(0)

// usage:
log_debug("x=%d\n", x);

这里有一些你可能不熟悉的“技巧”。

第一个是do{}while()循环。这是用于编写多语句宏的相当常见且基本上可移植的C语言。它允许您使用宏在;中终止它,并允许声明范围变量。然而,是一个陈述,而不是一个表达,所以没有“返回值”。通常对于像macros这样的printf()并不重要,但要注意,在尝试制作类似函数的宏时,这可能会让你感到厌烦。

另一个“技巧”是使用逗号吞噬'##'运算符。在此处阅读更多内容:https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

此外,这利用了背靠背字符串文字在C中连接形成单个字符串文字的事实。缺点是这种技术仅适用于字符串文字,即您的格式字符串不能在变量中。