M-am ciocnit de această ciudată macro cod in /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); }))
Ce inseamna :-!!
face?
Aceasta este, de fapt, o mod de a verifica dacă expresia e poate fi evaluată la 0, iar dacă nu, să nu construiască.
Macro este oarecum numitul, ar trebui să fie ceva mai mult ca BUILD_BUG_OR_ZERO
, mai degrabă decât ...ON_ZERO
. (Au fost ocazionale discuții despre dacă acest lucru este confuz nume.)
Tu ar trebui să citească expresia de genul asta:
sizeof(struct { int: -!!(e); }))
(e)
: Calcula expresia "e".
!!(e)
: Logic nega două ori: "0" dacă e == 0
; în caz contrar, 1
.
-!!(e)
: Numeric nega expresia de la pas 2: "0" dacă aceasta a fost 0
; în caz contrar, -1
.
struct{int: -!!(0);} --> struct{int: 0;}
: Dacă ar fi fost zero, atunci putem declara o struct cu un anonim întreg bitfield care are lățimea de zero. Totul este în regulă și vom proceda ca de obicei.
struct{int: -!!(1);} --> struct{int: -1;}
: Pe de altă parte, dacă isn't zero, atunci va fi un număr negativ. Declararea orice bitfield cu negative lățime este o eroare de compilare.
Așa că ne-am'll, fie vânt cu o bitfield care are lățimea de 0 într-un struct, ceea ce este bine, sau o bitfield cu negativ lățime, care este o eroare de compilare. Apoi ne-am lua sizeof
acest domeniu, așa că am obține un size_t cu lățimea corespunzătoare (care va fi zero, în cazul în care " e " este zero).
Unii oameni au întrebat: de Ce nu folosim doar un afirma
?
keithmo's a răspunde de aici are un răspuns bun:
Aceste macro-uri pună în aplicare o compilare-timp de testare, în timp ce afirma() este un run-time test.
Exact. Nu't vreau pentru a detecta problemele în kernel la execuție, care ar fi putut fi prins mai devreme! L's o piesă critică a sistemului de operare. În ce măsură problemele pot fi detectate în timpul compilării, cu atât mai bine.
A :
este o bitfield. Ca de !!
, care este logic dublă negație și astfel se întoarce " 0 "pentru fals sau" 1 " pentru adevărat. Și -
este un semn minus, adică aritmetica negație.
L's doar un truc pentru a obține compilatorul să vomite pe invalid intrări.
Consider BUILD_BUG_ON_ZERO
. Când -!!(e) se evaluează la o valoare negativă, care produce o eroare de compilare. În caz contrar,
-!!(e) se evaluează la 0 și o 0 lățime bitfield are dimensiunea de 0. Și, prin urmare, macro evaluează la un size_t cu valoarea 0.
Numele este slab din punctul meu de vedere, pentru că a construi, de fapt, nu atunci când de intrare este nu zero.
BUILD_BUG_ON_NULL
este foarte asemănătoare, dar dă un pointer, mai degrabă decât un int
.
Ei bine, eu sunt destul de surprins de faptul că alternativele la această sintaxă nu au fost menționate. O altă comună (dar mai mari) mecanism este de a apela o funcție care nu e't definite și se bazează pe instrumentul de optimizare a compila-funcția de apel dacă afirmația este corectă.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
În timp ce acest mecanism funcționează (atâta timp cât optimizări sunt activate) are dezavantajul de a nu raporta o eroare de până la tine link-ul, moment în care acesta nu reușește să găsească o definiție pentru funcția you_did_something_bad(). Ca's de ce kernel dezvoltatorii de pornire folosind trucuri, cum ar fi negativ dimensiuni pic-domeniul lățimi și negativ-de dimensiuni tablouri (mai târziu din care s-au mai spart construiește în GCC 4.4).
Din simpatie pentru nevoia de compilare-timp de afirmații, GCC 4.3 introdus de "eroare" funcția attribute, care vă permite să se extindă la această vârstă concept, dar a genera o compilare eroare cu un mesaj de alegerea ta ... nu mai criptic "negativ dimensiuni matrice" mesaje de eroare!
#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)
În fapt, ca de Linux 3.9, acum avem un macro numit compiletime_assert
care utilizează această caracteristică și de cele mai multe macro-uri în bug.h
au fost actualizate în consecință. Totuși, acest macro poate't fi folosit ca o inițializare. Cu toate acestea, utilizarea de declarație expresii (un alt CCG C-extensie), poți!
#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; \
})
Acest macro va evalua parametru o singură dată (în cazul în care nu are efecte secundare) și de a crea o compilare eroare care spune că "ți-am spus să nu-mi dea un cinci!" dacă expresie se evaluează la cinci sau nu este o compilare-timp constant.
Deci, de ce nu't ne folosind acest loc de negativ-dimensiuni pic-domenii? Din păcate, în prezent, există multe restricții de utilizare a declarației expresii, inclusiv utilizarea lor cât mai constantă de initializare (pentru enum constante, pic-lățime câmp, etc.) chiar dacă declarația de exprimare este complet constantă de sine (de exemplu, poate fi pe deplin evaluate la compilare-timp și în caz contrar trece __builtin_constant_p()
test). Mai mult, acestea nu pot fi utilizate în afara de o funcție a corpului.
Să sperăm că, GCC va modifica aceste neajunsuri în curând și permite declarație constant expresii pentru a fi folosit la fel de constant de initializare. Provocarea aici este limba specificații de definire a ceea ce este legal o expresie constantă. C++11, a adăugat constexpr cuvinte cheie pentru acest tip de lucru, dar nu omologul său există în C11. În timp ce C11 făcut statice afirmații, care va rezolva o parte din această problemă, nu se va rezolva toate aceste neajunsuri. Deci, eu sper că ccg poate face o constexpr funcționalitatea disponibil ca o extensie printr -std=gnuc99 & -std=gnuc11 sau așa ceva și permite utilizarea acestuia pe declarația expresii et. al.
L's a crea o dimensiune 0
bitfield dacă condiția este falsă, dar o dimensiune-1
(-!!1
) bitfield dacă condiția este adevărată/non-zero. În primul caz, nu există nici o eroare și struct este inițializat cu un int membru. În acest din urmă caz, există o eroare de compilare (și nu există un astfel de lucru ca o dimensiune -1
bitfield este creat, desigur).