Eu esbarrei neste estranho código macro em /usr/include/linux/kernel.h:
/* Force a compilation error if condition is true, but also produce a
result (of value 0 and type size_t), so the expression can be used
e.g. in a structure initializer (or where-ever else comma expressions
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
O que faz o :-!!
?
Esta é, com efeito, **uma forma de verificar se a expressão e pode ser avaliada como 0, e se não, para falhar a construção***.
A macro é um pouco mal chamada; deveria ser algo mais como BUILD_BUG_OR_ZERO
, em vez de ...ON_ZERO
. (Tem havido discussões ocasionais sobre se este é um nome confuso.)
Devias ler a expressão desta maneira:
sizeof(struct { int: -!!(e); }))
(e)
: Expressão computativa e
.
!!(e)
: Logicamente negar duas vezes: 0
se e == 0
; caso contrário 1
.
-!!(e)
: Nega numericamente a expressão do passo 2: 0
se fosse 0
; caso contrário -1
.
struct{int: -!!(0);} --> struct{int: 0;}
: Se era zero, então declaramos uma estrutura com um campo de bits inteiro anónimo que tem largura zero. Tudo está bem e procedemos normalmente.
struct{int: -!!(1);} --> struct{int: -1;}
: Por outro lado, se não for não for zero, então será um número negativo. Declarar qualquer bitfield com negative width é um erro de compilação.
Então vamos acabar com um bitfield que tem largura 0 em uma estrutura, o que é bom, ou um bitfield com largura negativa, que é um erro de compilação. Então pegamos sizeof
nesse campo, então obtemos um size_t
com a largura apropriada (que será zero no caso em que e
for zero).
Algumas pessoas têm perguntado: **"Por que não usar apenas um "assert"?
resposta de keithmo aqui tem uma boa resposta:
Estas macros implementam um teste em tempo de compilação, enquanto assert() é um teste em tempo de execução.
Exatamente certo. Você não quer detectar problemas no seu kernel em tempo de execução que poderiam ter sido detectados mais cedo! É uma peça crítica do sistema operacional. Seja qual for a extensão dos problemas que possam ser detectados em tempo de compilação, tanto melhor.
O :
é um bitfield. Quanto a !!
, isto é dupla negação lógica e assim retorna 0
para falso ou 1
para verdadeiro. E o -
é um sinal de menos, ou seja, negação aritmética.
É tudo apenas um truque para levar o compilador a vomitar em entradas inválidas.
Considere BUILD_BUG_ON_ZERO
. Quando -!!(e)
avalia para um valor negativo, isso produz um erro de compilação. Caso contrário o -!!(e)
avalia para 0, e um bitfield de largura 0 tem tamanho 0. E por isso a macro avalia para um size_t
com valor 0.
O nome é fraco no meu ponto de vista porque a construção de fato falha quando a entrada é não zero.
BUILD_BUG_ON_NULLé muito semelhante, mas produz um ponteiro em vez de um
int`.
Está criando um bitfield de tamanho 0
se a condição for falsa, mas um bitfield de tamanho -1
(-!!1
) se a condição for verdadeira/não-zero. No primeiro caso, não há erro e a estrutura é inicializada com um membro int. No segundo caso, há um erro de compilação (e não é criado um bitfield de tamanho -1
, é claro).