【问题标题】:How can I guarantee type safety of variadic arguments?如何保证可变参数的类型安全?
【发布时间】:2014-01-22 22:45:30
【问题描述】:

在 C 中,我想创建一个如下所示的函数或宏:

void Log(char* what, ...) 

其中 ... 必须是 const char* 的键值对。我真的希望不遵循此规则的代码在编译时爆炸。我尝试寻找执行此操作的 __attribute((..)) 指令,但无济于事。有什么想法吗?

【问题讨论】:

  • 你不能,抱歉。另外,不要忘记您需要一种方法来表明没有更多的值对(例如,具有空标记值)。
  • 最好传入一个数组或两个数组,如果你从一开始就知道你的参数需要是什么类型,那么使用可变参数函数没有任何意义。

标签: c variadic-functions variadic-macros


【解决方案1】:

C 无法对任意可变参数强制执行类型安全。 GCC 和 Clang 对于某些固定情况有__attribute__s(当可变参数函数期望最后一个参数是NULL,你可以使用__sentinel__;或者当它是一个格式字符串时,你可以使用format),但是没有通用的解决方案。

另一方面,C++11 有可变参数模板来解决这个问题,但既然你提到你正在使用 C,它就不适合你了。

【讨论】:

  • 是的,只是要使用一个以 NULL 结尾的数组。我已经在另一个版本中使用了 format 属性,但被要求为日志元数据添加键值对。
【解决方案2】:

其中 ... 必须是 const char* 的键值对

那么你根本不需要可变参数。如果你事先知道你期望的类型,那么你就不需要它,你只是让事情变得比他们需要的更困难。

只需传入一个数组即可。

void Log(const char *what, const char **args, size_t size);

【讨论】:

  • 有趣的是,我真的打算在这里使用可变参数日志功能。不过我真的很喜欢你的观点,你可能刚刚为我解决了另一个问题(即如何使用键值元数据进行 print-f 样式日志记录)。
  • @MarkPauley:对于那个,请查看带有valist 的包装器。所有接受可变参数的函数都应该提供一个。
【解决方案3】:

好的,我想出了一个解决方法:

#define ASSERT_LIST_TYPE(t, l...) do{t _t[] = {l};}while(0)

void _Log(char* what, ...);

#define Log(what, args...) do{\
  ASSERT_LIST_TYPE(char*, args);\
 _Log(what, args);\
}while(0)

如果 args 的类型不正确,这至少会生成警告,因为虚拟数组初始化的类型不正确。

如果这不是调试版本,我计划 #ifdef'ing ASSERT_LIST_TYPE 以防万一。

** 编辑 **

根据此处的反馈,我将代码更改为如下所示:

void _Log(char* what, const char** args, size_t args_len);

#define Log(what, args...) do{\
  const char* a[] = {args};\
  _Log(what, a, (sizeof a)/sizeof(char*));
}while(0)

即使 args 为空,这似乎也有效,并且它会捕获有人传递 obj-c 文字字符串而不是(@“...”而不是“...”),这是我之前的问题。

【讨论】:

  • 小心点:它会双重评估您的参数,一次用于数组初始化,一次用于将它们传递给函数。如果你只传递没有副作用的局部变量和表达式,你很好,但如果你直接传递一个函数调用,你可能会遇到比在可变参数函数调用中使用错误类型更难诊断的令人讨厌的问题。跨度>
猜你喜欢
  • 2018-10-25
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
  • 2011-04-28
相关资源
最近更新 更多