警告不是编译时常量的函数参数
Warn for function argument that is NOT a compile time constant
假设我正在维护一个接受两个参数的库函数,两个参数都是指针。第二个参数的存在只是为了向后兼容;调用者应始终传递 NULL。如果第二个参数不是 compile-time 常量 NULL,我想在我的 header 文件中添加一些内容,使编译器发出警告。我以为我可以使用 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。我收到 both 次调用 thefun
的警告。 (Compiler Explorer demo.)
任何人都可以提出一个替代结构,该结构会为 warning_expected
而不是 warning_not_expected
产生警告吗?
备注:
- 奇怪的是,上面的 does work 如果
b
是一个 int
。
- 以上使用 GCC-specific 扩展,但是欢迎使用适用于更广泛编译器的解决方案。 (特别是,clang 没有实现
attribute((warning))
,而且我还没有找到替代方案。)
- 一种在优化关闭时仍然有效的解决方案比关闭时仍然有效的解决方案更可取。 (即使
b
是一个 int
并且 thefun
被标记为 always-inline,以上内容在关闭优化的情况下不起作用。)
- 不涉及将
thefun
定义为宏的解决方案优于定义宏的解决方案。
- 当从 C 程序和 C++ 程序中包含时,header 必须工作。适量的 ifdeffage 是可以接受的。
- 它必须是警告,而不是硬错误,除非
-Werror
或等效项处于活动状态。
编辑: 基于 that the unwanted warning can be suppressed by casting the pointer to an integer of a different size, I have determined that this is an oversight in the implementation of __builtin_constant_p
and filed GCC bug report #91554。我仍然对提供使用 clang、icc 或任何其他通常与 GNU libc 一起使用的编译器执行此操作的方法的答案感兴趣。
如果没有 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 bug report #91554。
我终于成功了:
if (!__builtin_constant_p((int)(uintptr_t)b) || b != 0) {
这样你只会收到一个警告。
似乎gcc
不能对指针类型做__builtin_constant_p
。 __builtin_constant_p(b)
总是returns0,所以warn函数总是挂着的。将 b
转换为 int
的效果很奇怪。虽然它失去了指针值的精度,但我们不关心它,因为我们只检查它是否是一个常量。
假设我正在维护一个接受两个参数的库函数,两个参数都是指针。第二个参数的存在只是为了向后兼容;调用者应始终传递 NULL。如果第二个参数不是 compile-time 常量 NULL,我想在我的 header 文件中添加一些内容,使编译器发出警告。我以为我可以使用 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。我收到 both 次调用 thefun
的警告。 (Compiler Explorer demo.)
任何人都可以提出一个替代结构,该结构会为 warning_expected
而不是 warning_not_expected
产生警告吗?
备注:
- 奇怪的是,上面的 does work 如果
b
是一个int
。 - 以上使用 GCC-specific 扩展,但是欢迎使用适用于更广泛编译器的解决方案。 (特别是,clang 没有实现
attribute((warning))
,而且我还没有找到替代方案。) - 一种在优化关闭时仍然有效的解决方案比关闭时仍然有效的解决方案更可取。 (即使
b
是一个int
并且thefun
被标记为 always-inline,以上内容在关闭优化的情况下不起作用。) - 不涉及将
thefun
定义为宏的解决方案优于定义宏的解决方案。 - 当从 C 程序和 C++ 程序中包含时,header 必须工作。适量的 ifdeffage 是可以接受的。
- 它必须是警告,而不是硬错误,除非
-Werror
或等效项处于活动状态。
编辑: 基于 __builtin_constant_p
and filed GCC bug report #91554。我仍然对提供使用 clang、icc 或任何其他通常与 GNU libc 一起使用的编译器执行此操作的方法的答案感兴趣。
如果没有 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 bug report #91554。
我终于成功了:
if (!__builtin_constant_p((int)(uintptr_t)b) || b != 0) {
这样你只会收到一个警告。
似乎gcc
不能对指针类型做__builtin_constant_p
。 __builtin_constant_p(b)
总是returns0,所以warn函数总是挂着的。将 b
转换为 int
的效果很奇怪。虽然它失去了指针值的精度,但我们不关心它,因为我们只检查它是否是一个常量。