Bem, estou surpreso que as alternativas a essa sintaxe não tenham sido mencionadas. Outro mecanismo comum (mas mais antigo) é chamar uma função que não está definida e confiar no otimizador para compilar a chamada de função se sua afirmação estiver correta.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Embora esse mecanismo funcione (desde que as otimizações estejam ativadas), ele tem a desvantagem de não relatar um erro até você vincular, quando não consegue encontrar a definição para a função you_did_something_bad (). É por isso que os desenvolvedores do kernel começam a usar truques, como as larguras de campo de bits de tamanho negativo e as matrizes de tamanho negativo (o último dos quais parou de quebrar as compilações no GCC 4.4).
Simpatizando com a necessidade de asserções em tempo de compilação, o GCC 4.3 introduziu o error
atributo function que permite estender esse conceito mais antigo, mas gera um erro em tempo de compilação com uma mensagem de sua escolha - não mais uma matriz de tamanho negativo "enigmática" " mensagens de erro!
#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 fato, a partir do Linux 3.9, agora temos uma macro chamada compiletime_assert
que usa esse recurso e a maioria das macros bug.h
foram atualizadas de acordo. Ainda assim, essa macro não pode ser usada como inicializador. No entanto, usando expressões de declaração (outra extensão C do GCC), você pode!
#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; \
})
Essa macro avaliará seu parâmetro exatamente uma vez (caso tenha efeitos colaterais) e criará um erro em tempo de compilação que diz "Eu disse para você não me dar cinco!" se a expressão for avaliada como cinco ou não for uma constante em tempo de compilação.
Então, por que não estamos usando isso em vez de campos de bits de tamanho negativo? Infelizmente, atualmente existem muitas restrições ao uso de expressões de instrução, incluindo o uso como inicializadores constantes (para constantes enum, largura de campo de bits etc.), mesmo que a expressão de instrução seja completamente constante, ela própria (ou seja, pode ser totalmente avaliada em tempo de compilação e passa no __builtin_constant_p()
teste). Além disso, eles não podem ser usados fora de um corpo funcional.
Felizmente, o GCC alterará essas deficiências em breve e permitirá que expressões constantes de instruções sejam usadas como inicializadores constantes. O desafio aqui é a especificação da linguagem que define o que é uma expressão constante legal. O C ++ 11 adicionou a palavra-chave constexpr apenas para esse tipo ou coisa, mas nenhuma contrapartida existe no C11. Embora o C11 tenha obtido afirmações estáticas, o que resolverá parte desse problema, ele não resolverá todas essas deficiências. Então, espero que o gcc possa disponibilizar uma funcionalidade constexpr como uma extensão via -std = gnuc99 & -std = gnuc11 ou algo parecido e permitir seu uso nas expressões de declaração et. al.