【问题标题】:C Macro expansion into multiple function definitionsC宏扩展成多个函数定义
【发布时间】:2019-10-18 00:59:49
【问题描述】:

我有 3 个非常相似的日志函数,它们将从不同的地方调用。

void INFO (char *format, ...) {
   char log_buf[256] = {0};
   va_list args;
   va_start(args, format);
   vsprintf(log_buf, format, args);
   va_end(args);
   LIB_LOG_INFO(log_buf);
}

void ERROR (char *format, ...) {
   char log_buf[256] = {0};
   va_list args;
   va_start(args, format);
   vsprintf(log_buf, format, args);
   va_end(args);
   LIB_LOG_ERROR(log_buf);
}

void DEBUG (char *format, ...) {
   char log_buf[256] = {0};
   va_list args;
   va_start(args, format);
   vsprintf(log_buf, format, args);
   va_end(args);
   LIB_LOG_DEBUG(log_buf);
}

我不能传递任何额外的参数,所以我不能执行 if-else 来调用不同的 LIB_LOG 函数。

是否可以编写一个通用宏,在编译时扩展为这 3 个函数定义?只有 LIB_LOG 函数的名称不同。

【问题讨论】:

  • 使用 vsnprintf...
  • 你试过什么?我想你可能想要the ## (preprocessor token glue) operator
  • 为什么不直接使用这些函数呢?它们没那么贵。如果必须,请将它们设为static inline
  • edit 向我们展示使用代码的样子。那是使用宏的代码。

标签: c macros c-preprocessor


【解决方案1】:

首先请注意,变量参数函数几乎肯定是任何问题的错误解决方案。它们将被视为已弃用的危险功能 - 如果可能,请避免使用它们。

话虽如此,您可以通过制作一个带有函数指针参数的通用函数来解决您的问题:

typedef void lib_log_func_t (char* buf);

void lib_log (lib_log_func_t* func, const char* format, ...)
{
  char log_buf[256] = {0};
  va_list args;
  vsprintf(log_buf, format, args);
  va_end(args);
  func(log_buf);
}

然后编写3个包装宏:

#define INFO(format, ...)  lib_log(LIB_LOG_INFO,  format, __VA_ARGS__)
#define ERROR(format, ...) lib_log(LIB_LOG_ERROR, format, __VA_ARGS__)
#define DEBUG(format, ...) lib_log(LIB_LOG_ERROR, format, __VA_ARGS__)

【讨论】:

  • 我认为func参数应该从define的参数中去掉。顺便说一句,关于variable arguments are the wrong solutions,如果您想制作类似printf 的功能,它们是必要的。特别是,对于这个用例,GCC 有一个编译器属性来检查传递给函数的参数是否与 printf 系列所需的参数匹配:__attribute_((__format__(__printf__)))
  • @LoPiTaL 是的,当然,这是一个错字 - 已修复,谢谢。不,我们不应该制作类似 printf 的函数。 stdio.h 通常应避免在生产代码中使用 - 这些是计算机历史上设计最糟糕的一些功能,当回顾它们造成的错误和损害的数量时。
  • 包装宏假定LIB_LOG_INFO 等是函数而不是类似函数的宏。全部大写的名称暗示了更多的宏——这可能不是一个安全的假设。
  • @Lundin 我们不应该制作类似 printf 的函数我必须不同意你的观点,至少在你可以展示另一种创建可用日志记录函数的方法之前不必依赖应用程序级锁定来确保原子写入日志文件。 infoLogEntryWithTwoStringsOneIntOneUnsignedLongAndFourDoubles() 及其所有必需的兄弟是不现实的,也不是强迫调用者手动构造一个字符串 - 无论如何,解决方案很可能是 s[n]printf()
  • @Lundin 我猜你错过了我的编辑 - 因为调用者将使用s[n]printf() 构造字符串。这将导致大量重复工作,可能会导致大量错误,否则这些错误可以在经过良好测试和彻底审查的日志库函数中完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-16
  • 2014-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多