用宏包装函数(不重命名)C

我有兴趣在现有函数调用周围添加一些额外的逻辑,通过包装它们而不重命名它们。 (仅用于测试)

我发现的现有解决方案依赖于将函数包装在不同名称的宏中,这可能意味着更改了大量代码。

有什么建议?


注意,我知道LD_PRELOAD ,但我有兴趣使用宏来检查传递给函数的参数(例如使用_Generic )。

通常不需要重命名宏包装函数,因为在宏的扩展中,宏的名称不会扩展。

以下是C11标准的一个例子:

 #define cbrt(X) _Generic((X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \ )(X) 

当然,如果你有#include ,你会遇到问题,因为在这种情况下你已经有了类型通用宏,比如上面的那些,你将无法重新定义sqrt (除非你#undef它)。 在定义该宏之前,您必须#include

即使这样,你也在踩着薄冰。 该标准保留了标准库函数的名称,并坚持(§7.1.3/ 2):

如果程序在保留它的上下文中声明或定义标识符(除了7.1.4允许的标识符),或者将保留标识符定义为宏名称,则行为是未定义的。

引用的7.1.4节允许你#undef一个类似于函数的宏来影响标准库函数,但是我不清楚你是否允许随后重新定义它。 因人而异。


如果您想使用_Generic来调用包装器函数,您仍然可以在不重命名原始函数的情况下完成该工作。 例如:

 #include  /* In this file, you can't call nanny(char), and if you call it * with an unsigned argument, we'll insert an additional check */ #define nanny(X) _Generic((X), int: nanny, unsigned:nanny_wrapper)(X) int nanny_wrapper(unsigned x) { assert(x < INT_MAX); /* The redundant parentheses prevent macro expansion */ return (nanny)(x); } 

例如,假设我想检查函数调用上的类型。

一个简单的例子,我想检查sqrt只接受( intdouble ),而不是float

这是一个正常的方法。

 /* add this to a header or at the top of the source-code */ #include  static typeof(&sqrt) sqrt_wrap = sqrt; #define sqrt(f) (_Generic((f), int: sqrt_wrap, double: sqrt_wrap))(f) 

这有效,但有一些缺点。

  • 必须包含 ,或者至少定义sqrt因为我们不知道是否调用了静态函数。
  • 定义一个可能未使用的函数。 虽然__attribute__((__unused__))适用于GCC / Clang。
  • 即使最终不使用sqrt ,所有输出也必须能够访问函数符号。

编辑!


这确实有效,首先需要包含标题(显然 – 回想起来)

 #include  #define sqrt(f) _Generic((f), int: sqrt(f), double: sqrt(f)) 

下面是一个检查sqrt的技巧不是用float调用的,而sqrtf不是用double调用的

 #include  #define sqrt(X) _Generic((X), float: NULL, default: sqrt)(X) #define sqrtf(X) _Generic((X), double: NULL, default: sqrtf)(X)