Ну, я досить здивований, що альтернативи цьому синтаксису не згадані. Інший поширений (але старший) механізм - викликати функцію, яка не визначена, і покладатися на оптимізатор для складання виклику функції, якщо ваше твердження правильне.
#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
них були оновлені відповідно. Проте цей макрос не можна використовувати як ініціалізатор. Однак, використовуючи вирази операторів (ще одне розширення GCC C), ви можете!
#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 або якусь подібну та дозволити її використання у виразах висловлювань тощо. ін.