Was ist der beste Weg, um statische Zusicherungen zur Kompilierungszeit in C (nicht in C ++) zu erzielen, wobei der Schwerpunkt auf GCC liegt?
c
gcc
assert
compile-time
static-assert
Matt Joiner
quelle
quelle
_Static_assert
Teil des C11-Standards ist und jeder Compiler, der C11 unterstützt, es haben wird.error: expected declaration specifiers or '...' before 'sizeof'
für Zeile bekommestatic_assert( sizeof(int) == sizeof(long int), "Error!);
(ich benutze übrigens C, nicht C ++)_Static_assert( sizeof(int) == sizeof(long int), "Error!");
Auf meinem Macine wird der Fehler angezeigt.error: expected declaration specifiers or '...' before 'sizeof'
ANDerror: expected declaration specifiers or '...' before string constant
(er bezieht sich auf die"Error!"
Zeichenfolge) (auch: Ich kompiliere mit -std = c11. Wenn die Deklaration in eine Funktion eingefügt wird, funktioniert alles gut (schlägt fehl und ist wie erwartet erfolgreich))_Static_assert
nicht den C ++ - Standardstatic_assert
. Sie müssen `#include <assert.h> verwenden, um das static_assert-Makro abzurufen.Dies funktioniert im Funktions- und Nichtfunktionsbereich (jedoch nicht innerhalb von Strukturen, Gewerkschaften).
Wenn die Zusicherung der Kompilierungszeit nicht übereinstimmen konnte, wird von GCC eine fast verständliche Nachricht generiert
sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative
Das Makro könnte oder sollte geändert werden, um einen eindeutigen Namen für das typedef zu generieren (dh
__LINE__
am Ende desstatic_assert_...
Namens verketten )Anstelle eines ternären kann auch dieser verwendet werden,
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]
der sogar auf dem rostigen alten cc65-Compiler (für den 6502-CPU) funktioniert.UPDATE: Der Vollständigkeit halber hier die Version mit
__LINE__
UPDATE2: GCC-spezifischer Code
GCC 4.3 (ich denke) hat die Funktionsattribute "Fehler" und "Warnung" eingeführt. Wenn ein Aufruf einer Funktion mit diesem Attribut nicht durch Eliminierung des toten Codes (oder andere Maßnahmen) beseitigt werden konnte, wird ein Fehler oder eine Warnung generiert. Dies kann verwendet werden, um Assets zur Kompilierungszeit mit benutzerdefinierten Fehlerbeschreibungen zu erstellen. Es bleibt zu bestimmen, wie sie im Namespace-Bereich verwendet werden können, ohne auf eine Dummy-Funktion zurückzugreifen:
Und so sieht es aus:
quelle
-Og
) reicht jedoch häufig aus, damit dies funktioniert, und sollte das Debuggen nicht beeinträchtigen. Man kann in Betracht ziehen, die statische Zusicherung zu einer No-Op- oder Laufzeit-Zusicherung zu machen, wenn__OPTIMIZE__
(und__GNUC__
) nicht definiert ist.__LINE__
Version in gcc 4.1.1 ... mit gelegentlichem Ärger, wenn zwei verschiedene Header zufällig einen in derselben nummerierten Zeile haben!cl
Ich weiß, dass in der Frage gcc ausdrücklich erwähnt wird, aber der Vollständigkeit halber ist hier eine Optimierung für Microsoft-Compiler.
Die Verwendung des negativ dimensionierten Arrays typedef überzeugt cl nicht , einen anständigen Fehler auszuspucken. Es heißt nur
error C2118: negative subscript
. Ein Bitfeld mit einer Breite von Null ist in dieser Hinsicht besser. Da dies das Typendeffing einer Struktur beinhaltet, müssen wir wirklich eindeutige Typnamen verwenden.__LINE__
schneidet den Senf nicht - es ist möglich, dassCOMPILE_TIME_ASSERT()
in einem Header und einer Quelldatei ein in derselben Zeile steht, und Ihre Kompilierung wird unterbrochen.__COUNTER__
kommt zur Rettung (und es ist in gcc seit 4.3).Jetzt
unter
cl
gibt:Gcc gibt auch eine verständliche Nachricht:
quelle
Aus Wikipedia :
quelle
Ich würde NICHT empfehlen, die Lösung mit einem zu verwenden
typedef
:Es
typedef
wird NICHT garantiert, dass die Array-Deklaration mit dem Schlüsselwort zur Kompilierungszeit ausgewertet wird. Beispielsweise wird der folgende Code im Blockbereich kompiliert:Ich würde dies stattdessen empfehlen (auf C99):
Aufgrund des
static
Schlüsselworts wird das Array zur Kompilierungszeit definiert. Beachten Sie, dass diese Zusicherung nur funktioniert, mitCOND
denen zur Kompilierungszeit ausgewertet wird. Es funktioniert nicht mit Bedingungen (dh die Kompilierung schlägt fehl) mit Bedingungen, die auf Werten im Speicher basieren, z. B. Werten, die Variablen zugewiesen sind.quelle
Bei Verwendung des Makros STATIC_ASSERT () mit
__LINE__
, können Sie Zeilennummernkonflikte zwischen einem Eintrag in einer .c-Datei und einem anderen Eintrag in einer Header-Datei vermeiden, indem Sie einschließen__INCLUDE_LEVEL__
.Zum Beispiel :
quelle
Der klassische Weg ist die Verwendung eines Arrays:
Es funktioniert, weil wenn die Behauptung wahr ist, das Array die Größe 1 hat und gültig ist, aber wenn es falsch ist, gibt die Größe -1 einen Kompilierungsfehler aus.
Die meisten Compiler zeigen den Namen der Variablen an und zeigen auf den rechten Teil des Codes, wo Sie eventuelle Kommentare zur Zusicherung hinterlassen können.
quelle
#define STATIC_ASSERT()
allgemeinere Beispiele und Beispiel-Compiler-Ausgaben aus Ihren generischen BeispielenSTATIC_ASSERT()
bereitstellen, erhalten Sie viel mehr Upvotes und diese Technik macht meiner Meinung nach mehr Sinn.Von Perl, speziell
perl.h
Zeile 3455 (<assert.h>
ist vorher enthalten):Wenn
static_assert
verfügbar (von<assert.h>
), wird es verwendet. Andernfalls wird, wenn die Bedingung falsch ist, ein Bitfeld mit einer negativen Größe deklariert, wodurch die Kompilierung fehlschlägt.STMT_START
/STMT_END
sind Makros, die aufdo
/ erweitertwhile (0)
werden.quelle
Weil:
_Static_assert()
ist jetzt in gcc für alle Versionen von C und definiertstatic_assert()
ist in C ++ 11 und höher definiertDas folgende einfache Makro für
STATIC_ASSERT()
funktioniert daher in:g++ -std=c++11
) oder höhergcc -std=c90
gcc -std=c99
gcc -std=c11
gcc
(kein Standard angegeben)Definieren Sie
STATIC_ASSERT
wie folgt:Verwenden Sie es jetzt:
Beispiele:
Getestet in Ubuntu mit gcc 4.8.4:
Beispiel 1: Gute
gcc
Ausgabe (dh: DieSTATIC_ASSERT()
Codes funktionieren, aber die Bedingung war falsch und verursachte eine Bestätigung zur Kompilierungszeit):Beispiel 2: Gute
g++ -std=c++11
Ausgabe (dh: DieSTATIC_ASSERT()
Codes funktionieren, aber die Bedingung war falsch und verursachte eine Bestätigung zur Kompilierungszeit):Beispiel 3: Fehlgeschlagene C ++ - Ausgabe (dh: Der Assert-Code funktioniert überhaupt nicht richtig, da hierfür eine Version von C ++ vor C ++ 11 verwendet wird):
Vollständige Testergebnisse hier:
Verbunden:
quelle
static_assert
Makro drin istassert.h
?static_assert()
ist es in C überhaupt nicht verfügbar. Siehe auch hier: en.cppreference.com/w/cpp/language/static_assert - es zeigtstatic_assert
, dass "(seit C ++ 11)" vorhanden ist. Das Schöne an meiner Antwort ist, dass es in gccs C90 und höher sowie in C ++ 11 und höher funktioniert, anstatt nur in C ++ 11 und höherstatic_assert()
. Was ist an meiner Antwort kompliziert? Es sind nur ein paar#define
s.static_assert
ist in C seit C11 definiert. Es ist ein Makro, das erweitert wird_Static_assert
. en.cppreference.com/w/c/error/static_assert . Zusätzlich und im Gegensatz zu Ihrer Antwort_Static_assert
ist in c99 und c90 in gcc nicht verfügbar (nur in gnu99 und gnu90). Dies entspricht dem Standard. Grundsätzlich erledigen Sie eine Menge zusätzlicher Arbeit, die nur dann Vorteile bringt, wenn sie mit gnu90 und gnu99 kompiliert wird, und die den tatsächlichen Anwendungsfall unbedeutend klein macht.Für diejenigen unter Ihnen, die etwas wirklich Grundlegendes und Tragbares wollen, aber keinen Zugriff auf C ++ 11-Funktionen haben, habe ich genau das Richtige geschrieben.
Verwenden
STATIC_ASSERT
Sie es normal (Sie können es zweimal in derselben Funktion schreiben, wenn Sie möchten) und verwenden Sie esGLOBAL_STATIC_ASSERT
außerhalb von Funktionen mit einer eindeutigen Phrase als erstem Parameter.Erläuterung:
Zuerst wird geprüft, ob Sie die echte Zusicherung haben, die Sie definitiv verwenden möchten, wenn sie verfügbar ist.
Wenn Sie dies nicht tun, wird es
pred
bestätigt, indem Sie Ihr Eis erhalten und es durch sich selbst teilen. Dies macht zwei Dinge.Wenn es Null ist, id est, ist die Zusicherung fehlgeschlagen, führt dies zu einem Fehler beim Teilen durch Null (die Arithmetik wird erzwungen, weil versucht wird, ein Array zu deklarieren).
Wenn es nicht Null ist, normalisiert es die Arraygröße auf
1
. Wenn die Behauptung bestanden wurde, möchten Sie nicht, dass sie trotzdem fehlschlägt, da Ihr Prädikat als-1
(ungültig) oder232442
(massive Platzverschwendung, IDK, wenn es optimiert würde) bewertet wird.Da
STATIC_ASSERT
es in geschweifte Klammern gesetzt ist, ist es ein Block, der die Variable abdecktassert
Das heißt, Sie können es viele Male schreiben.Es wirft es auch auf
void
Dies ist ein bekannter Weg, umunused variable
Warnungen loszuwerden .Für
GLOBAL_STATIC_ASSERT
statt in einem Codeblock zu sein, erzeugt er einen Namespace. Namespaces sind außerhalb von Funktionen zulässig. Einunique
Bezeichner ist erforderlich, um widersprüchliche Definitionen zu stoppen, wenn Sie diese mehrmals verwenden.Arbeitete für mich an GCC und VS'12 C ++
quelle
Dies funktioniert mit dem Optionssatz "Nicht verwendete entfernen". Ich kann eine globale Funktion verwenden, um globale Parameter zu überprüfen.
quelle
Dies funktionierte für einige alte gcc. Entschuldigung, dass ich vergessen habe, um welche Version es sich handelt:
quelle