Nun, ich bin ziemlich überrascht, dass die Alternativen zu dieser Syntax nicht erwähnt wurden. Ein weiterer gängiger (aber älterer) Mechanismus besteht darin, eine nicht definierte Funktion aufzurufen und sich beim Kompilieren des Funktionsaufrufs auf den Optimierer zu verlassen, wenn Ihre Behauptung korrekt ist.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Während dieser Mechanismus funktioniert (solange Optimierungen aktiviert sind), hat er den Nachteil, dass er erst beim Verknüpfen einen Fehler meldet. Zu diesem Zeitpunkt findet er die Definition für die Funktion you_did_something_bad () nicht. Aus diesem Grund beginnen Kernel-Entwickler mit Tricks wie den Bitfeldbreiten mit negativer Größe und den Arrays mit negativer Größe (von denen die späteren Builds in GCC 4.4 nicht mehr brechen).
In Sympathie für die Notwendigkeit von Zusicherungen zur Kompilierungszeit hat GCC 4.3 das error
Funktionsattribut eingeführt , mit dem Sie dieses ältere Konzept erweitern können, aber einen Fehler zur Kompilierungszeit mit einer Nachricht Ihrer Wahl generieren können - kein kryptisches Array mit negativer Größe mehr " Fehlermeldungen!
#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)
Tatsächlich haben wir ab Linux 3.9 ein Makro namens compiletime_assert
, das diese Funktion verwendet, und die meisten Makros bug.h
wurden entsprechend aktualisiert. Dieses Makro kann jedoch nicht als Initialisierer verwendet werden. Mit Anweisungsausdrücken (einer anderen GCC C-Erweiterung) können Sie jedoch!
#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; \
})
Dieses Makro wertet seinen Parameter genau einmal aus (falls es Nebenwirkungen hat) und erstellt einen Fehler bei der Kompilierung, der besagt: "Ich habe dir gesagt, du sollst mir keine Fünf geben!" wenn der Ausdruck fünf ergibt oder keine Kompilierungszeitkonstante ist.
Warum verwenden wir dies nicht anstelle von Bitfeldern mit negativer Größe? Leider gibt es derzeit viele Einschränkungen bei der Verwendung von Anweisungsausdrücken, einschließlich ihrer Verwendung als konstante Initialisierer (für Enum-Konstanten, Bitfeldbreite usw.), selbst wenn der Anweisungsausdruck selbst vollständig konstant ist (dh vollständig ausgewertet werden kann) zur Kompilierungszeit und besteht ansonsten den __builtin_constant_p()
Test). Außerdem können sie nicht außerhalb eines Funktionskörpers verwendet werden.
Hoffentlich wird GCC diese Mängel bald ändern und die Verwendung konstanter Anweisungsausdrücke als konstante Initialisierer ermöglichen. Die Herausforderung hierbei ist die Sprachspezifikation, die definiert, was ein rechtlicher konstanter Ausdruck ist. C ++ 11 hat das Schlüsselwort constexpr nur für diesen Typ oder dieses Objekt hinzugefügt, aber in C11 ist kein Gegenstück vorhanden. Während C11 statische Zusicherungen erhalten hat, die einen Teil dieses Problems lösen, werden nicht alle diese Mängel behoben. Daher hoffe ich, dass gcc eine constexpr-Funktionalität als Erweiterung über -std = gnuc99 & -std = gnuc11 oder eine solche zur Verfügung stellen und deren Verwendung für Anweisungsausdrücke et ermöglichen kann. al.