Statische Behauptung in C.

85

Was ist der beste Weg, um statische Zusicherungen zur Kompilierungszeit in C (nicht in C ++) zu erzielen, wobei der Schwerpunkt auf GCC liegt?

Matt Joiner
quelle

Antworten:

87

Der C11-Standard fügt das _Static_assertSchlüsselwort hinzu.

Dies ist seit gcc-4.6 implementiert :

_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */

Der erste Slot muss ein integraler konstanter Ausdruck sein. Der zweite Slot ist ein konstantes String-Literal, das lang sein kann ( _Static_assert(0, L"assertion of doom!")).

Ich sollte beachten, dass dies auch in neueren Versionen von clang implementiert ist.

emsr
quelle
4
[... scheint von gcc, von clang implementiert zu sein ...] Sie können sicherer sein , dass das ;-) _Static_assertTeil des C11-Standards ist und jeder Compiler, der C11 unterstützt, es haben wird.
PP
1
Kann dies im Dateibereich verwendet werden (außerhalb einer Funktion)? Weil ich error: expected declaration specifiers or '...' before 'sizeof'für Zeile bekomme static_assert( sizeof(int) == sizeof(long int), "Error!); (ich benutze übrigens C, nicht C ++)
user10607
@ user10607 Ich bin überrascht, dass dies nicht funktioniert. Warten Sie, Sie verpassen ein Zitat am Ende Ihrer Fehlerzeichenfolge. Setzen Sie das ein und kommen Sie zurück. Dies funktioniert bei mir unter gcc-4.9: _Static_assert( sizeof(int) == sizeof(long int), "Error!");Auf meinem Macine wird der Fehler angezeigt.
Emsr
Ich habe gcc 4.8.2 unter Ubuntu. Das fehlende Zitat war ein Tippfehler (ich hatte es im Code). Dies ist die erste Zeile in einer Datei, nachdem einige Header enthalten sind. Der Compiler gibt mir genau zwei Fehler: error: expected declaration specifiers or '...' before 'sizeof'AND error: 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))
user10607
2
@ user10607 Ich musste auch -std = gnu11 in der Kommandozeile angeben. Ich bin wirklich überrascht, dass es einen Unterschied zwischen 4,8 und 4,8 geben würde. Ich habe eine Quelle mit nur einer Zeile. Ich habe auch den C-Standard verwendet, _Static_assertnicht den C ++ - Standard static_assert. Sie müssen `#include <assert.h> verwenden, um das static_assert-Makro abzurufen.
Emsr
92

Dies funktioniert im Funktions- und Nichtfunktionsbereich (jedoch nicht innerhalb von Strukturen, Gewerkschaften).

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

STATIC_ASSERT(1,this_should_be_true); 

int main()
{
 STATIC_ASSERT(1,this_should_be_true); 
}
  1. 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

  2. Das Makro könnte oder sollte geändert werden, um einen eindeutigen Namen für das typedef zu generieren (dh __LINE__am Ende des static_assert_...Namens verketten )

  3. 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__

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
// token pasting madness:
#define COMPILE_TIME_ASSERT3(X,L) STATIC_ASSERT(X,static_assertion_at_line_##L)
#define COMPILE_TIME_ASSERT2(X,L) COMPILE_TIME_ASSERT3(X,L)
#define COMPILE_TIME_ASSERT(X)    COMPILE_TIME_ASSERT2(X,__LINE__)

COMPILE_TIME_ASSERT(sizeof(long)==8); 
int main()
{
    COMPILE_TIME_ASSERT(sizeof(int)==4); 
}

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:

#define CTC(X) ({ extern int __attribute__((error("assertion failure: '" #X "' not true"))) compile_time_check(); ((X)?0:compile_time_check()),0; })

// never to be called.    
static void my_constraints()
{
CTC(sizeof(long)==8); 
CTC(sizeof(int)==4); 
}

int main()
{
}

Und so sieht es aus:

$ gcc-mp-4.5 -m32 sas.c 
sas.c: In function 'myc':
sas.c:7:1: error: call to 'compile_time_check' declared with attribute error: assertion failure: `sizeof(int)==4` not true
Nordischer Mainframe
quelle
1
In Visual Studio steht nur "Negativer Index", ohne den Variablennamen zu erwähnen ...
szx
Nordic Mainframe - Option 3 in Ihrer Antwort funktioniert nicht bei Clang.
Elazar
1
In Bezug auf die letzte (GCC 4.3 + -spezifische) Lösung: Dies ist sehr leistungsfähig, da sie alles überprüfen kann, was der Optimierer herausfinden kann, aber fehlschlägt, wenn die Optimierung nicht aktiviert ist. Die Mindestoptimierungsstufe ( -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.
Søren Løvborg
Im Code-Snippet mit LINE-Version (UPDATE: Der Vollständigkeit halber ist hier die Version mit `LINE) beim Kompilieren Fehler in der Zeile (STATIC_ASSERT (X, static_assertion_at_line _ ## L)), die durch Hinzufügen einer weiteren korrigiert werden kann Level wie folgt: #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); #define COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, "" Assertion at: ## L "");
Sundar
Ich verwende etwas Ähnliches wie die __LINE__Version in gcc 4.1.1 ... mit gelegentlichem Ärger, wenn zwei verschiedene Header zufällig einen in derselben nummerierten Zeile haben!
MM
11

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, dass COMPILE_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).

#define CTASTR2(pre,post) pre ## post
#define CTASTR(pre,post) CTASTR2(pre,post)
#define STATIC_ASSERT(cond,msg) \
    typedef struct { int CTASTR(static_assertion_failed_,msg) : !!(cond); } \
        CTASTR(static_assertion_failed_,__COUNTER__)

Jetzt

STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)

unter clgibt:

Fehler C2149: 'static_assertion_failed_use_another_compiler_luke': Das benannte Bitfeld darf keine Breite von Null haben

Gcc gibt auch eine verständliche Nachricht:

Fehler: Nullbreite für Bitfeld 'static_assertion_failed_use_another_compiler_luke'

Bobbogo
quelle
4

Aus Wikipedia :

#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
Tyler
quelle
15
Es wäre besser, wenn Sie auf die wahre Quelle verlinken würden
Matt Joiner
In gcc 4.6 funktioniert es nicht - es heißt "case label reduziert sich nicht auf eine ganzzahlige Konstante". Es hat einen Punkt.
Liosan
Sie haben beide wahrscheinlich schon weitergezogen, aber am Ende habe ich meine eigene geschrieben (siehe meine Antwort ). Ich habe Ihren Link @MattJoiner verwendet, um mir zu helfen
Hashbrown
Und wenn Sie sich Sorgen machen können, lassen Sie mich wissen, ob es für Sie funktioniert, @Liosan. Ich habe gerade erst angefangen, mich mit C ++ zu beschäftigen, also bin ich zu spät zur Party gekommen
Hashbrown
Visual C ++ ist seit Version 2010 in static_assert integriert und funktioniert sowohl im C ++ - als auch im c-Modus. Der c99 _Static_assert ist jedoch nicht integriert.
ddbug
3

Ich würde NICHT empfehlen, die Lösung mit einem zu verwenden typedef:

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

Es typedefwird NICHT garantiert, dass die Array-Deklaration mit dem Schlüsselwort zur Kompilierungszeit ausgewertet wird. Beispielsweise wird der folgende Code im Blockbereich kompiliert:

int invalid_value = 0;
STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);

Ich würde dies stattdessen empfehlen (auf C99):

#define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]

Aufgrund des staticSchlüsselworts wird das Array zur Kompilierungszeit definiert. Beachten Sie, dass diese Zusicherung nur funktioniert, mit CONDdenen 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.

FredFredFredFred
quelle
4
Dies würde zwar funktionieren, aber auch Ihren Speicherbedarf erhöhen.
Sherellbc
Fehler: 'static_assertion_INVALID_CHAR_SIZE' definiert, aber nicht verwendet [-Werror = unused-variable]
Alex
2

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 :

/* Trickery to create a unique variable name */
#define BOOST_JOIN( X, Y )      BOOST_DO_JOIN( X, Y )
#define BOOST_DO_JOIN( X, Y )   BOOST_DO_JOIN2( X, Y )
#define BOOST_DO_JOIN2( X, Y )  X##Y
#define STATIC_ASSERT(x)        typedef char \
        BOOST_JOIN( BOOST_JOIN(level_,__INCLUDE_LEVEL__), \
                    BOOST_JOIN(_assert_on_line_,__LINE__) ) [(x) ? 1 : -1]
BrentNZ
quelle
1

Der klassische Weg ist die Verwendung eines Arrays:

char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];

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.

Paolo.Bolzoni
quelle
Wenn Sie dies in ein generisches Typmakro einwickeln und #define STATIC_ASSERT()allgemeinere Beispiele und Beispiel-Compiler-Ausgaben aus Ihren generischen Beispielen STATIC_ASSERT()bereitstellen, erhalten Sie viel mehr Upvotes und diese Technik macht meiner Meinung nach mehr Sinn.
Gabriel Staples
Ich stimme nicht zu Der Compiler sieht Gedankenmakros und gibt eine verwirrendere Nachricht.
Paolo.Bolzoni
1

Von Perl, speziell perl.hZeile 3455 ( <assert.h>ist vorher enthalten):

/* STATIC_ASSERT_DECL/STATIC_ASSERT_STMT are like assert(), but for compile
   time invariants. That is, their argument must be a constant expression that
   can be verified by the compiler. This expression can contain anything that's
   known to the compiler, e.g. #define constants, enums, or sizeof (...). If
   the expression evaluates to 0, compilation fails.
   Because they generate no runtime code (i.e.  their use is "free"), they're
   always active, even under non-DEBUGGING builds.
   STATIC_ASSERT_DECL expands to a declaration and is suitable for use at
   file scope (outside of any function).
   STATIC_ASSERT_STMT expands to a statement and is suitable for use inside a
   function.
*/
#if (defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L)) && (!defined(__IBMC__) || __IBMC__ >= 1210)
/* static_assert is a macro defined in <assert.h> in C11 or a compiler
   builtin in C++11.  But IBM XL C V11 does not support _Static_assert, no
   matter what <assert.h> says.
*/
#  define STATIC_ASSERT_DECL(COND) static_assert(COND, #COND)
#else
/* We use a bit-field instead of an array because gcc accepts
   'typedef char x[n]' where n is not a compile-time constant.
   We want to enforce constantness.
*/
#  define STATIC_ASSERT_2(COND, SUFFIX) \
    typedef struct { \
        unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
    } _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
#  define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
#  define STATIC_ASSERT_DECL(COND)    STATIC_ASSERT_1(COND, __LINE__)
#endif
/* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
   error (static_assert is a declaration, and only statements can have labels).
*/
#define STATIC_ASSERT_STMT(COND)      STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END

Wenn static_assertverfü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_ENDsind Makros, die auf do/ erweitert while (0)werden.

Melpomene
quelle
1

Weil:

  1. _Static_assert() ist jetzt in gcc für alle Versionen von C und definiert
  2. static_assert() ist in C ++ 11 und höher definiert

Das folgende einfache Makro für STATIC_ASSERT()funktioniert daher in:

  1. C ++:
    1. C ++ 11 ( g++ -std=c++11) oder höher
  2. C:
    1. gcc -std=c90
    2. gcc -std=c99
    3. gcc -std=c11
    4. gcc (kein Standard angegeben)

Definieren Sie STATIC_ASSERTwie folgt:

/* For C++: */
#ifdef __cplusplus
    #ifndef _Static_assert
        #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
    #endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")

Verwenden Sie es jetzt:

STATIC_ASSERT(1 > 2); // Output will look like: error: static assertion failed: "(1 > 2) failed" 

Beispiele:

Getestet in Ubuntu mit gcc 4.8.4:

Beispiel 1: Gute gccAusgabe (dh: Die STATIC_ASSERT()Codes funktionieren, aber die Bedingung war falsch und verursachte eine Bestätigung zur Kompilierungszeit):

$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: In Funktion 'main'
static_assert.c: 78: 38: Fehler: statische Zusicherung fehlgeschlagen: "(1> 2) fehlgeschlagen"
#define STATIC_ASSERT (test_for_true ) _Static_assert ((test_for_true), "(" #test_for_true ") fehlgeschlagen")
^
static_assert.c: 88: 5: Hinweis: bei Erweiterung des Makros 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Beispiel 2: Gute g++ -std=c++11Ausgabe (dh: Die STATIC_ASSERT()Codes funktionieren, aber die Bedingung war falsch und verursachte eine Bestätigung zur Kompilierungszeit):

$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c: In Funktion 'int main ()'
static_assert.c: 74: 32: Fehler: statische Zusicherung fehlgeschlagen: (1> 2) fehlgeschlagen
#define _Static_assert static_assert / * static_assertist Teil von C ++ 11 oder höher * /
^
static_assert.c: 78: 38: Hinweis: in Erweiterung des Makros '_Static_assert'
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") failed")
^
static_assert.c: 88: 5: Hinweis: bei Erweiterung des Makros 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

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):

$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 88: 5: Warnung: Bezeichner 'static_assert' ist ein Schlüsselwort in C ++ 11 [-Wc ++ 0x-kompatibel]
STATIC_ASSERT (1> 2 );
^
static_assert.c: In der Funktion 'int main ()'
static_assert.c: 78: 99: Fehler: 'static_assert' wurde in diesem Bereich nicht deklariert
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") ) fehlgeschlagen ")
^
static_assert.c: 88: 5: Hinweis: bei Erweiterung des Makros 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Vollständige Testergebnisse hier:

/*
static_assert.c
- test static asserts in C and C++ using gcc compiler

Gabriel Staples
4 Mar. 2019 

To be posted in:
1. /programming/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
2. /programming/3385515/static-assert-in-c/7287341#7287341

To compile & run:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert

-------------
TEST RESULTS:
-------------

1. `_Static_assert(false, "1. that was false");` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  NO

2. `static_assert(false, "2. that was false");` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             NO
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    NO
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    NO
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    NO
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES

3. `STATIC_ASSERT(1 > 2);` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES

*/

#include <stdio.h>
#include <stdbool.h>

/* For C++: */
#ifdef __cplusplus
    #ifndef _Static_assert
        #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
    #endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")


int main(void)
{
    printf("Hello World\n");

    /*_Static_assert(false, "1. that was false");*/
    /*static_assert(false, "2. that was false");*/

    STATIC_ASSERT(1 > 2);

    return 0;
}

Verbunden:

  1. Verwenden Sie static_assert, um die an das Makro übergebenen Typen zu überprüfen [meine eigene Antwort]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. Verwenden Sie static_assert, um die an das Makro übergebenen Typen zu überprüfen
  3. Verwendung der statischen Zusicherung in C zum Überprüfen der an ein Makro übergebenen Parametertypen
Gabriel Staples
quelle
1
Warum so kompliziert, wenn ein static_assertMakro drin ist assert.h?
Auf Wiedersehen SE
@KamiKaze, ich bin überrascht von Ihrer Frage, da Sie meine Antwort anscheinend nicht gelesen haben? Die zweite Zeile meiner Antwort sagt alles: "static_assert () ist in C ++ 11 und höher definiert". Daher static_assert()ist es in C überhaupt nicht verfügbar. Siehe auch hier: en.cppreference.com/w/cpp/language/static_assert - es zeigt static_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öher static_assert(). Was ist an meiner Antwort kompliziert? Es sind nur ein paar #defines.
Gabriel Staples
static_assertist 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_assertist 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.
Auf Wiedersehen SE
> "_Static_assert ist in c99 und c90 in gcc nicht verfügbar (nur in gnu99 und gnu90)". Ich verstehe was du meinst. Es ist eine gcc-Erweiterung, damit Sie richtig liegen. > "Grundsätzlich erledigen Sie viel zusätzliche Arbeit". Ich bin nicht einverstanden; 2 extrem einfache Definitionen sind keineswegs "viel" zusätzliche Arbeit. Davon abgesehen verstehe ich, was du jetzt meinst. Ich denke immer noch, dass das, was ich getan habe, nützlich ist und einen Mehrwert für das hier vorgestellte Wissen und die Antworten bietet, daher denke ich nicht, dass es die Abwertung verdient. Mein Fehler, "C90 und höher" anstelle von "gcc C90 und später" oder "g90 und später" zu sagen, war nur in meinem obigen Kommentar und nicht in meiner Antwort.
Gabriel Staples
Da es sachlich falsch war, war eine Ablehnung gerechtfertigt. Wenn Sie die falschen Aussagen korrigieren würden, werde ich die Antwort erneut prüfen und möglicherweise meine Ablehnung zurückziehen. Das Hinzufügen eines solchen Codes, falls dies nicht erforderlich ist (wenn Sie also nicht mit gnu90 und gnu99 arbeiten), ist aus Gründen der Klarheit nicht vorteilhaft und sorgt für mehr Unordnung. Wenn Sie den Anwendungsfall haben, könnte es sich lohnen. Aber ich wundere mich über die Seltenheit des Verwendungszwecks, in dem die Kompatibilität von gnu99 / 90 und c ++ 11 erforderlich ist.
Auf Wiedersehen SE
0

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_ASSERTSie es normal (Sie können es zweimal in derselben Funktion schreiben, wenn Sie möchten) und verwenden Sie es GLOBAL_STATIC_ASSERTaußerhalb von Funktionen mit einer eindeutigen Phrase als erstem Parameter.

#if defined(static_assert)
#   define STATIC_ASSERT static_assert
#   define GLOBAL_STATIC_ASSERT(a, b, c) static_assert(b, c)
#else
#   define STATIC_ASSERT(pred, explanation); {char assert[1/(pred)];(void)assert;}
#   define GLOBAL_STATIC_ASSERT(unique, pred, explanation); namespace ASSERTATION {char unique[1/(pred)];}
#endif

GLOBAL_STATIC_ASSERT(first, 1, "Hi");
GLOBAL_STATIC_ASSERT(second, 1, "Hi");

int main(int c, char** v) {
    (void)c; (void)v;
    STATIC_ASSERT(1 > 0, "yo");
    STATIC_ASSERT(1 > 0, "yo");
//    STATIC_ASSERT(1 > 2, "yo"); //would compile until you uncomment this one
    return 0;
}

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 predbestä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) oder 232442(massive Platzverschwendung, IDK, wenn es optimiert würde) bewertet wird.
Da STATIC_ASSERTes in geschweifte Klammern gesetzt ist, ist es ein Block, der die Variable abdecktassertDas heißt, Sie können es viele Male schreiben.
Es wirft es auch aufvoidDies ist ein bekannter Weg, um unused variableWarnungen loszuwerden .
Für GLOBAL_STATIC_ASSERTstatt in einem Codeblock zu sein, erzeugt er einen Namespace. Namespaces sind außerhalb von Funktionen zulässig. Ein uniqueBezeichner ist erforderlich, um widersprüchliche Definitionen zu stoppen, wenn Sie diese mehrmals verwenden.


Arbeitete für mich an GCC und VS'12 C ++

Hashbrown
quelle
1
Es gibt keine Namespaces in C.
Martinkunev
Ah, hoppla, habe die Frage falsch verstanden. Sieht so aus, als wäre ich hierher gekommen, um nach einer Antwort auf C ++ zu suchen
siehe
0

Dies funktioniert mit dem Optionssatz "Nicht verwendete entfernen". Ich kann eine globale Funktion verwenden, um globale Parameter zu überprüfen.

//
#ifndef __sassert_h__
#define __sassert_h__

#define _cat(x, y) x##y

#define _sassert(exp, ln) \
extern void _cat(ASSERT_WARNING_, ln)(void); \
if(!(exp)) \
{ \
    _cat(ASSERT_WARNING_, ln)(); \
}

#define sassert(exp) _sassert(exp, __LINE__)

#endif //__sassert_h__

//-----------------------------------------
static bool tab_req_set_relay(char *p_packet)
{
    sassert(TXB_TX_PKT_SIZE < 3000000);
    sassert(TXB_TX_PKT_SIZE >= 3000000);
    ...
}

//-----------------------------------------
Building target: ntank_app.elf
Invoking: Cross ARM C Linker
arm-none-eabi-gcc ...
../Sources/host_if/tab_if.c:637: undefined reference to `ASSERT_WARNING_637'
collect2: error: ld returned 1 exit status
make: *** [ntank_app.elf] Error 1
//
user4978854
quelle
1
Wenn es überhaupt funktioniert, würde es dies nur in der Quelle einer ausführbaren Datei tun.
Coder
0

Dies funktionierte für einige alte gcc. Entschuldigung, dass ich vergessen habe, um welche Version es sich handelt:

#define _cat(x, y) x##y

#define _sassert(exp, ln)\
extern char _cat(SASSERT_, ln)[1]; \
extern char _cat(SASSERT_, ln)[exp ? 1 : 2]

#define sassert(exp) _sassert((exp), __LINE__)

//
sassert(1 == 2);

//
#148 declaration is incompatible with "char SASSERT_134[1]" (declared at line 134)  main.c  /test/source/controller line 134    C/C++ Problem
Jay
quelle