Ну, я весьма удивлен, что альтернативы этому синтаксису не были упомянуты. Другим распространенным (но более старым) механизмом является вызов функции, которая не определена, и использование оптимизатора для компиляции вызова функции, если ваше утверждение верно.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Хотя этот механизм работает (при условии, что оптимизация включена), его недостатком является то, что он не сообщает об ошибке до тех пор, пока вы не создадите ссылку, и в этот момент он не может найти определение функции you_did_something_bad (). Вот почему разработчики ядра начали использовать такие приемы, как ширина битового поля отрицательного размера и массивы отрицательного размера (последний из которых прекратил ломать сборки в GCC 4.4).
Сочувствуя необходимости утверждений во время компиляции, GCC 4.3 представил error
атрибут функции, который позволяет вам расширить эту более старую концепцию, но генерирует ошибку времени компиляции с сообщением по вашему выбору - не более загадочный массив отрицательного размера " Сообщения об ошибках!
#define MAKE_SURE_THIS_IS_FIVE(number) \
do { \
extern void this_isnt_five(void) __attribute__((error( \
"I asked for five and you gave me " #number))); \
if ((number) != 5) \
this_isnt_five(); \
} while (0)
Фактически, начиная с Linux 3.9, теперь у нас есть макрос, compiletime_assert
который использует эту функцию, и большинство макросов bug.h
были соответствующим образом обновлены. Тем не менее, этот макрос не может быть использован в качестве инициализатора. Однако, используя выражения выражений (другое C-расширение GCC), вы можете!
#define ANY_NUMBER_BUT_FIVE(number) \
({ \
typeof(number) n = (number); \
extern void this_number_is_five(void) __attribute__(( \
error("I told you not to give me a five!"))); \
if (n == 5) \
this_number_is_five(); \
n; \
})
Этот макрос оценит свой параметр ровно один раз (в случае, если у него есть побочные эффекты) и создаст ошибку во время компиляции, которая говорит: «Я сказал вам не давать мне пять!» если выражение оценивается как пять или не является константой времени компиляции.
Так почему же мы не используем это вместо битовых полей отрицательного размера? Увы, в настоящее время существует много ограничений на использование выражений операторов, в том числе их использование в качестве инициализаторов констант (для констант перечисления, ширины битового поля и т. Д.), Даже если выражение оператора полностью само по себе (т. Е. Может быть полностью оценено). во время компиляции и в противном случае проходит __builtin_constant_p()
тест). Кроме того, они не могут использоваться вне функционального тела.
Надеемся, что GCC вскоре исправит эти недостатки и позволит использовать выражения константных выражений в качестве инициализаторов констант. Проблема здесь заключается в спецификации языка, определяющей, что является законным постоянным выражением. C ++ 11 добавил ключевое слово constexpr только для этого типа или вещи, но в C11 не существует аналога. Хотя C11 получил статические утверждения, которые решат часть этой проблемы, он не решит все эти недостатки. Поэтому я надеюсь, что gcc может сделать функциональность constexpr доступной как расширение через -std = gnuc99 & -std = gnuc11 или что-то подобное и разрешить его использование в выражениях выражений et. и др.