Предупредить об аргументе функции, который НЕ является константой времени компиляции
Предположим, я поддерживаю библиотечную функцию, которая принимает два аргумента, оба указателя. Второй аргумент существует только для обратной совместимости; абоненты должны всегда передавать 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
или его эквивалент не активен.
ОБНОВЛЕНИЕ: На основании открытия Камила Цука о том, что нежелательное предупреждение можно подавить, приведя указатель к целому числу другого размера, я определил, что это упущение при реализации __builtin_constant_p
и подал отчет об ошибке GCС# 91554. Мне все еще будут интересны ответы, которые предоставляют способы сделать это с помощью clang, icc или любого другого компилятора, который обычно используется вместе с GNU libc.
Ответы
Ответ 1
Мне наконец удалось заставить его работать:
if (!__builtin_constant_p((int)(uintptr_t)b) || b != 0) {
При этом вы получаете только одно предупреждение.
Кажется, что gcc
не может сделать __builtin_constant_p
для типа указателя. __builtin_constant_p(b)
всегда возвращает 0, поэтому функция warn всегда связана. Приведение b
к int
странно работает. Хотя он теряет точность в значении указателя, нас это не волнует, потому что мы только проверяем, является ли оно константой.
Ответ 2
Невозможно сделать то, что вы описываете, без расширений 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.
См. отчет об ошибках GCС# 91554 для получения дополнительной информации о необходимости приведений.