警告不是编译时常量的函数参数

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 产生警告吗?

备注:


编辑: 基于 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 的效果很奇怪。虽然它失去了指针值的精度,但我们不关心它,因为我们只检查它是否是一个常量。