Bueno, estoy bastante sorprendido de que no se hayan mencionado las alternativas a esta sintaxis. Otro mecanismo común (pero más antiguo) es llamar a una función que no está definida y confiar en el optimizador para compilar la llamada a la función si su afirmación es correcta.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Si bien este mecanismo funciona (siempre y cuando las optimizaciones estén habilitadas) tiene el inconveniente de no informar un error hasta que se enlace, momento en el cual no puede encontrar la definición de la función you_did_something_bad (). Es por eso que los desarrolladores de kernel comienzan a usar trucos como los anchos de campo de bits de tamaño negativo y las matrices de tamaño negativo (la última de las cuales dejó de romper las compilaciones en GCC 4.4).
En simpatía por la necesidad de afirmaciones en tiempo de compilación, GCC 4.3 introdujo el error
atributo de función que le permite ampliar este concepto anterior, pero generar un error en tiempo de compilación con un mensaje de su elección: no más críptico "matriz de tamaño negativo" " ¡error de mensajes!
#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)
De hecho, a partir de Linux 3.9, ahora tenemos una macro llamada compiletime_assert
que utiliza esta función y la mayoría de las macros bug.h
se han actualizado en consecuencia. Aún así, esta macro no se puede usar como inicializador. Sin embargo, si usa expresiones de enunciado (otra extensión C de GCC), ¡puede hacerlo!
#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; \
})
Esta macro evaluará su parámetro exactamente una vez (en caso de que tenga efectos secundarios) y creará un error en tiempo de compilación que dice "¡Te dije que no me dieras cinco!" si la expresión se evalúa a cinco o no es una constante en tiempo de compilación.
Entonces, ¿por qué no estamos usando esto en lugar de campos de bits de tamaño negativo? Por desgracia, actualmente existen muchas restricciones en el uso de expresiones de enunciados, incluido su uso como inicializadores constantes (para constantes enum, ancho de campo de bits, etc.) incluso si la expresión de enunciados es completamente constante en sí misma (es decir, puede evaluarse completamente en tiempo de compilación y de lo contrario pasa la __builtin_constant_p()
prueba). Además, no se pueden usar fuera del cuerpo de una función.
Con suerte, GCC enmendará estas deficiencias pronto y permitirá que se utilicen expresiones de enunciados constantes como inicializadores constantes. El desafío aquí es la especificación del lenguaje que define lo que es una expresión legal constante. C ++ 11 agregó la palabra clave constexpr solo para este tipo o cosa, pero no existe una contraparte en C11. Si bien C11 obtuvo aserciones estáticas, que resolverán parte de este problema, no resolverá todas estas deficiencias. Así que espero que gcc pueda hacer que una funcionalidad constexpr esté disponible como una extensión a través de -std = gnuc99 & -std = gnuc11 o algo así y permitir su uso en expresiones de declaración et. Alabama.