Ну, я очень удивлен, что альтернативы этому синтаксису не были упомянуты. Другим распространенным (но более старым) механизмом является вызов функции, которая не определена, и использование оптимизатора для компиляции вызова функции, если ваше утверждение верно.
#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. и др.