假设我正在维护一个库函数,该函数接受两个参数,两个指针。 第二个参数仅用于向后兼容;调用方应始终传递 NULL。我想在我的头文件中放一些东西,如果第二个参数不是编译时常量 NULL,编译器会发出警告。 我想我可以使用 GCC 的__builtin_constant_p
和__attribute__((warning))
扩展来做到这一点:
extern void thefun_called_with_nonnull_arg (void)
__attribute__((__warning__(
"'thefun' called with second argument not NULL")));
extern int real_thefun (void *, void *);
static inline int
thefun (void *a, void *b)
{
if (!__builtin_constant_p(b) || b != 0)
thefun_called_with_nonnull_arg();
return real_thefun(a, b);
}
int warning_expected (void *a, void *b)
{
return thefun(a, b);
}
int warning_not_expected (void *a)
{
return thefun(a, 0);
}
但这不适用于我测试过的任何版本的 GCC。 我收到两个呼叫thefun
的警告。 (编译器资源管理器演示。
谁能提出一个替代结构,为warning_expected
而不是warning_not_expected
产生警告?
笔记:
- 奇怪的是,如果
b
是一个int
,上述方法确实有效。 - 以上使用特定于 GCC 的扩展,但是欢迎适用于更广泛种类的编译器的解决方案。 (特别是,clang 没有实现
attribute((warning))
,我没有找到替代方案。
关闭优化 - 后仍然有效的解决方案比关闭优化的解决方案更可取。 (上述内容在关闭优化的情况下不起作用,即使
b
是int
并且thefun
标记为始终内联。 - 不涉及将
thefun
定义为宏的解决方案比将宏定义为宏的解决方案更可取。 - 当从 C 程序和C++程序中包含标头时,标头必须有效。 适度的 ifdeffage 是可以接受的。
- 它必须是警告,而不是硬错误,除非
-Werror
或等效项处于活动状态。
编辑:基于Kamil Cuk的发现,可以通过将指针投射到不同大小的整数来抑制不需要的警告,我已经确定这是实施__builtin_constant_p
和提交的GCC错误报告#91554的疏忽。 我仍然对提供clang,icc或任何其他通常与GNU libc一起使用的编译器的方法的答案感兴趣。
我终于设法让它工作了:
if (!__builtin_constant_p((int)(uintptr_t)b) || b != 0) {
这样,您只会收到一个警告。
似乎gcc
无法对指针类型执行__builtin_constant_p
。__builtin_constant_p(b)
始终返回 0,因此 warn 函数始终是链接的。将b
选为int
很奇怪。虽然它失去了指针值的精度,但我们并不关心它,因为我们只检查它是否是一个常量。
没有GNU扩展就没有办法做你所描述的。
这种可移植方法会产生硬错误(因为_Static_assert
需要一个常量表达式):
#define thefun(a, b)
({
_Static_assert(b == 0,
"'thefun' called with second argument not NULL");
real_thefun(a, b);
})
但是,有一种强化风格的方法适用于 GCC 和 Clang:
extern void thefun_called_with_nonnull_arg (void)
__attribute__((__deprecated__(
"'thefun' called with second argument not NULL")));
extern int real_thefun (void *, void *);
static inline int
thefun (void *a, void *b)
{
if (!__builtin_constant_p((unsigned short)(unsigned long)b) || b != 0)
thefun_called_with_nonnull_arg();
return real_thefun(a, b);
}
int warning_expected (void *a, void *b)
{
return thefun(a, b);
}
int warning_not_expected (void *a)
{
return thefun(a, 0);
}
使用 GCC 8.3.0 和 Clang 8.0.0 进行测试。
请参阅 GCC 错误报告 #91554 了解有关强制转换需求的更多信息。