Eh bien, je suis assez surpris que les alternatives à cette syntaxe n'aient pas été mentionnées. Un autre mécanisme courant (mais plus ancien) consiste à appeler une fonction qui n'est pas définie et à s'appuyer sur l'optimiseur pour compiler l'appel de fonction si votre assertion est correcte.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Bien que ce mécanisme fonctionne (tant que les optimisations sont activées), il a l'inconvénient de ne pas signaler d'erreur jusqu'à ce que vous établissiez un lien, auquel cas il ne trouve pas la définition de la fonction you_did_something_bad (). C'est pourquoi les développeurs du noyau commencent à utiliser des astuces comme les largeurs de champ de bits de taille négative et les tableaux de taille négative (dont le dernier a cessé de casser les builds dans GCC 4.4).
Par sympathie pour le besoin d'assertions au moment de la compilation, GCC 4.3 a introduit l' error
attribut de fonction qui vous permet d'étendre sur ce concept plus ancien, mais de générer une erreur au moment de la compilation avec un message de votre choix - plus de tableau de taille négative "plus cryptique" " messages d'erreur!
#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)
En fait, depuis Linux 3.9, nous avons maintenant une macro appelée compiletime_assert
qui utilise cette fonctionnalité et la plupart des macros dans bug.h
ont été mises à jour en conséquence. Pourtant, cette macro ne peut pas être utilisée comme initialiseur. Cependant, en utilisant des expressions par instruction (une autre extension C de GCC), vous pouvez!
#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; \
})
Cette macro évaluera son paramètre exactement une fois (au cas où elle aurait des effets secondaires) et créera une erreur de compilation qui dit "Je vous ai dit de ne pas m'en donner cinq!" si l'expression est évaluée à cinq ou n'est pas une constante de temps de compilation.
Alors pourquoi n'utilisons-nous pas cela au lieu de champs binaires de taille négative? Hélas, il existe actuellement de nombreuses restrictions à l'utilisation des expressions d'instruction, y compris leur utilisation comme initialiseurs constants (pour les constantes d'énumération, la largeur du champ de bits, etc.) même si l'expression d'instruction est complètement constante elle-même (c'est-à-dire qu'elle peut être entièrement évaluée au moment de la compilation et sinon passe le __builtin_constant_p()
test). De plus, ils ne peuvent pas être utilisés en dehors d'un corps de fonction.
Avec un peu de chance, GCC modifiera bientôt ces lacunes et permettra d'utiliser des expressions d'instruction constantes comme initialiseurs constants. Le défi ici est la spécification du langage définissant ce qu'est une expression constante légale. C ++ 11 a ajouté le mot clé constexpr pour ce type ou cette chose, mais aucune contrepartie n'existe en C11. Bien que C11 ait obtenu des assertions statiques, ce qui résoudra une partie de ce problème, il ne résoudra pas tous ces défauts. J'espère donc que gcc pourra rendre une fonctionnalité constexpr disponible en tant qu'extension via -std = gnuc99 & -std = gnuc11 ou quelque chose du genre et permettre son utilisation sur les expressions d'instructions et. Al.