Bene, sono abbastanza sorpreso che le alternative a questa sintassi non siano state menzionate. Un altro meccanismo comune (ma meno recente) è chiamare una funzione non definita e fare affidamento sull'ottimizzatore per compilare la chiamata di funzione se la tua affermazione è corretta.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Mentre questo meccanismo funziona (fintanto che le ottimizzazioni sono abilitate) ha il rovescio della medaglia di non segnalare un errore fino al collegamento, a quel punto non riesce a trovare la definizione per la funzione you_did_something_bad (). Ecco perché gli sviluppatori del kernel iniziano a usare trucchi come le larghezze di campo bit di dimensioni negative e le matrici di dimensioni negative (la parte successiva ha smesso di interrompere le build in GCC 4.4).
In sintonia con la necessità di asserzioni in fase di compilazione, GCC 4.3 ha introdotto l' error
attributo della funzione che consente di estendere questo concetto precedente, ma di generare un errore in fase di compilazione con un messaggio di propria scelta - non più "criptico" array di dimensioni negative " messaggio di errore!
#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)
In effetti, a partire da Linux 3.9, ora abbiamo una macro chiamata compiletime_assert
che utilizza questa funzionalità e la maggior parte delle macro inbug.h
sono state aggiornate di conseguenza. Tuttavia, questa macro non può essere utilizzata come inizializzatore. Tuttavia, usando le espressioni statement (un'altra estensione C di GCC), puoi!
#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; \
})
Questa macro valuterà il suo parametro esattamente una volta (nel caso abbia effetti collaterali) e creerà un errore di compilazione che dice "Ti ho detto di non darmi un cinque!" se l'espressione restituisce cinque o non è una costante di compilazione.
Quindi perché non stiamo usando questo invece di campi bit di dimensioni negative? Purtroppo, al momento ci sono molte restrizioni all'uso delle espressioni delle istruzioni, incluso il loro uso come inizializzatori costanti (per costanti di enum, larghezza del campo di bit, ecc.) Anche se l'espressione dell'istruzione è completamente costante il suo sé (cioè, può essere valutato completamente in fase di compilazione e in caso contrario supera il __builtin_constant_p()
test). Inoltre, non possono essere utilizzati al di fuori di un corpo di funzione.
Eventualmente, GCC modificherà presto queste carenze e consentirà di utilizzare espressioni di istruzioni costanti come inizializzatori costanti. La sfida qui è la specifica del linguaggio che definisce ciò che è un'espressione costante legale. C ++ 11 ha aggiunto la parola chiave constexpr proprio per questo tipo o cosa, ma non esiste alcuna controparte in C11. Mentre C11 ha ottenuto affermazioni statiche, che risolveranno parte di questo problema, non risolverà tutte queste carenze. Quindi spero che gcc possa rendere disponibile una funzionalità constexpr come estensione tramite -std = gnuc99 & -std = gnuc11 o alcuni di questi e consentirne l'uso su espressioni di istruzioni et. al.