Cóż, jestem dość zaskoczony, że nie wspomniano o alternatywach dla tej składni. Innym powszechnym (ale starszym) mechanizmem jest wywoływanie funkcji, która nie została zdefiniowana i poleganie na optymalizatorze w celu skompilowania wywołania funkcji, jeśli twoje twierdzenie jest poprawne.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Chociaż ten mechanizm działa (o ile włączone są optymalizacje), jego wadą jest nie zgłaszanie błędu do momentu połączenia, w którym to czasie nie można znaleźć definicji funkcji you_did_something_bad (). Dlatego programiści jądra zaczynają używać sztuczek, takich jak szerokości pola bitowego o ujemnej wielkości i tablice o rozmiarze ujemnym (późniejsze przestały łamać kompilacje w GCC 4.4).
W odpowiedzi na potrzebę asercji w czasie kompilacji, GCC 4.3 wprowadził error
atrybut funkcji, który pozwala rozwinąć tę starszą koncepcję, ale generuje błąd czasu kompilacji z wybranym przez Ciebie komunikatem - nigdy więcej tajemniczej „ujemnej tablicy” „komunikaty o błędach!
#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)
W rzeczywistości, począwszy od Linuksa 3.9, mamy teraz wywoływane makro, compiletime_assert
które korzysta z tej funkcji, a większość makr bug.h
została odpowiednio zaktualizowana. Tego makra nie można jednak używać jako inicjalizatora. Jednak używając wyrażeń instrukcji (inne rozszerzenie C GCC), możesz!
#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; \
})
To makro dokona oceny swojego parametru dokładnie raz (w przypadku, gdy ma skutki uboczne) i utworzy błąd kompilacji, który mówi: „Mówiłem, żebyś nie dawał mi pięciu!” jeśli wyrażenie ma wartość pięć lub nie jest stałą czasu kompilacji.
Dlaczego więc nie używamy tego zamiast ujemnych pól bitowych? Niestety, istnieje obecnie wiele ograniczeń użycia wyrażeń instrukcji, w tym ich użycia jako stałych inicjatorów (dla stałych wyliczeniowych, szerokości pola bitowego itp.), Nawet jeśli wyrażenie instrukcji jest całkowicie stałe, to znaczy, że można je w pełni ocenić w czasie kompilacji i w inny sposób przechodzi __builtin_constant_p()
test). Ponadto nie można ich używać poza ciałem funkcji.
Mamy nadzieję, że GCC niedługo poprawi te niedociągnięcia i zezwoli na stosowanie stałych wyrażeń instrukcji jako stałych inicjatorów. Wyzwaniem jest tutaj specyfikacja języka określająca, co jest stałym wyrażeniem prawnym. C ++ 11 dodał słowo kluczowe constexpr tylko dla tego typu lub rzeczy, ale w C11 nie istnieje odpowiednik. Podczas gdy C11 otrzymał statyczne twierdzenia, które rozwiążą część tego problemu, nie rozwiąże wszystkich tych niedociągnięć. Mam więc nadzieję, że gcc może udostępnić funkcjonalność constexpr jako rozszerzenie poprzez -std = gnuc99 i -std = gnuc11 lub inne, i umożliwić jej użycie w wyrażeniach instrukcji et. glin.