Предупредить об аргументе функции, который НЕ является константой времени компиляции

Предположим, я поддерживаю библиотечную функцию, которая принимает два аргумента, оба указателя. Второй аргумент существует только для обратной совместимости; абоненты должны всегда передавать 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 для получения дополнительной информации о необходимости приведений.