Asercja statyczna w C

86

Jaki jest najlepszy sposób na osiągnięcie statycznych potwierdzeń czasu kompilacji w C (nie C ++), ze szczególnym uwzględnieniem GCC?

Matt Joiner
źródło

Odpowiedzi:

90

Standard C11 dodaje _Static_assertsłowo kluczowe.

Jest to zaimplementowane od gcc-4.6 :

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

Pierwsza szczelina musi być integralnym wyrażeniem stałym. Drugi slot to stały literał ciągu, którym może być long ( _Static_assert(0, L"assertion of doom!")).

Powinienem zauważyć, że jest to również zaimplementowane w najnowszych wersjach clang.

emsr
źródło
4
[... wydaje się być zaimplementowane przez gcc, przez clang ...] Możesz być bardziej asertywny , że ;-) _Static_assertjest częścią standardu C11 i każdy kompilator obsługujący C11 będzie go miał.
PP
1
Czy można tego użyć w zakresie pliku (poza jakąkolwiek funkcją)? Ponieważ otrzymuję error: expected declaration specifiers or '...' before 'sizeof'linię static_assert( sizeof(int) == sizeof(long int), "Error!); (przy okazji używam C, a nie C ++)
user10607
@ user10607 Dziwię się, że to nie działa. Czekaj, brakuje cudzysłowu na końcu łańcucha błędu. Włóż to i wracaj. Działa to dla mnie na gcc-4.9: _Static_assert( sizeof(int) == sizeof(long int), "Error!");Na moim komputerze pojawia się błąd.
emsr
Mam gcc 4.8.2 na Ubuntu. Brakujący cytat to literówka w komentarzu (miałem go w kodzie). To jest pierwsza linia w pliku po kilku dołączeniach nagłówka. Kompilator podaje mi dwa dokładnie takie same błędy: error: expected declaration specifiers or '...' before 'sizeof'AND error: expected declaration specifiers or '...' before string constant(odwołuje się do ciągu "Error!"znaków) (również: kompiluję z -std = c11. Podczas umieszczania deklaracji wewnątrz funkcji wszystko działa dobrze (kończy się niepowodzeniem i kończy się sukcesem zgodnie z oczekiwaniami))
user10607
2
@ user10607 Musiałem również podać -std = gnu11 w wierszu poleceń. Jestem naprawdę zaskoczony, że byłaby różnica między 4,8 a 4,8. Mam źródło z tylko jedną linią. Użyłem też standardu C, a _Static_assertnie języka C ++ static_assert. Musisz `#include <assert.h>, aby uzyskać makro static_assert.
emsr
93

Działa to w zakresie funkcyjnym i niefunkcyjnym (ale nie wewnątrz struktur, związków).

#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. Jeśli asercji czasu kompilacji nie można dopasować, GCC generuje prawie zrozumiały komunikat sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative

  2. Makro można lub należy zmienić, aby wygenerować unikalną nazwę dla typu (tj. Konkatenację __LINE__na końcu static_assert_...nazwy)

  3. Zamiast trójskładnika można go również użyć, #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]co zdarza się, że działa nawet na zardzewiałym, starym kompilatorze cc65 (dla procesora 6502).

AKTUALIZACJA: Ze względu na kompletność, oto wersja z__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: kod specyficzny dla GCC

GCC 4.3 (chyba) wprowadziło atrybuty funkcji „error” i „warning”. Jeśli wywołanie funkcji z tym atrybutem nie mogło zostać wyeliminowane poprzez eliminację martwego kodu (lub inne środki), generowany jest błąd lub ostrzeżenie. Może to służyć do tworzenia potwierdzeń czasu kompilacji ze zdefiniowanymi przez użytkownika opisami niepowodzeń. Pozostaje ustalić, jak można ich używać w zakresie przestrzeni nazw bez uciekania się do funkcji fikcyjnej:

#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()
{
}

A tak to wygląda:

$ 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
Nordic Mainframe
źródło
1
W programie Visual Studio jest po prostu napisane „Negative subscript”, nie wspominając o nazwie zmiennej ...
szx
Nordic Mainframe - opcja 3 w Twojej odpowiedzi nie działa na clang.
Elazar
1
Odnośnie ostatniego rozwiązania (specyficznego dla GCC 4.3 +): Jest to bardzo potężne rozwiązanie, ponieważ może sprawdzić wszystko, co może wykryć optymalizator, ale kończy się niepowodzeniem, jeśli optymalizacja nie jest włączona. Zwykły minimalny poziom optymalizacji ( -Og) może jednak często wystarczać, aby to zadziałało i nie powinien kolidować z debugowaniem. Można rozważyć uczynienie ze statycznego potwierdzenia braku operacji lub działania wykonawczego, jeśli __OPTIMIZE__(i __GNUC__) nie jest zdefiniowane.
Søren Løvborg
We fragmencie kodu z wersją LINE (UPDATE: Ze względu na kompletność, oto wersja z `LINE), podczas kompilacji zawiera błędy w wierszu (STATIC_ASSERT (X, static_assertion_at_line _ ## L)), który można poprawić dodając jeszcze jeden poziom jak poniżej: #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); # zdefiniować COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, "" Assertion at: ## L "");
niedziela
Używam czegoś podobnego do __LINE__wersji w gcc 4.1.1 ... z czasami irytacją, gdy dwa różne nagłówki mają jeden w tej samej numerowanej linii!
MM,
10

kl

Wiem, że pytanie wyraźnie wspomina o gcc, ale dla kompletności tutaj jest poprawka dla kompilatorów Microsoft.

Użycie ujemnego typu array typedef nie przekonuje cl do wyplucia przyzwoitego błędu. Po prostu mówi error C2118: negative subscript. Pod tym względem pole bitowe o zerowej szerokości wypada lepiej. Ponieważ obejmuje to typedeffing struktury, naprawdę musimy używać unikalnych nazw typów. __LINE__nie tnie musztardy - możliwe jest umieszczenie COMPILE_TIME_ASSERT()w tej samej linii nagłówka i pliku źródłowego, a kompilacja się zepsuje. __COUNTER__przychodzi na ratunek (i jest w gcc od 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__)

Teraz

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

pod cldaje:

błąd C2149: „static_assertion_failed_use_another_compiler_luke”: nazwane pole bitowe nie może mieć zerowej szerokości

Gcc daje również zrozumiały komunikat:

błąd: zerowa szerokość dla pola bitowego „static_assertion_failed_use_another_compiler_luke”

bobbogo
źródło
4

Z Wikipedii :

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

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
Tyler
źródło
15
Byłoby lepiej, gdybyś utworzył
Matt Joiner
Nie działa w gcc 4.6 - mówi, że „etykieta przypadku nie redukuje się do stałej liczby całkowitej”. Ma rację.
Liosan
oboje prawdopodobnie już ruszyliście dalej, ale w końcu napisałem własne (zobacz moją odpowiedź ). Użyłem twojego linku @MattJoiner, aby mi pomóc
Hashbrown,
A jeśli przeszkadza ci to, daj mi znać, czy to działa w twoim przypadku, @Liosan. Dopiero co zacząłem zagłębiać się w C ++, więc spóźniłem się na imprezę
Hashbrown
Jeśli chodzi o Visual C ++, ma on wbudowany static_assert od wersji 2010 i działa zarówno w trybach c ++, jak i c. Jednak nie ma wbudowanej funkcji c99 _Static_assert.
ddbug
3

Chciałbym NIE zalecamy użycie rozwiązanie wykorzystujące typedef:

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

Deklaracja tablicy ze typedefsłowem kluczowym NIE gwarantuje, że zostanie oceniona w czasie kompilacji. Na przykład następujący kod w zakresie blokowym zostanie skompilowany:

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

Poleciłbym to zamiast tego (na C99):

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

Ze względu na staticsłowo kluczowe tablica zostanie zdefiniowana w czasie kompilacji. Zauważ, że to potwierdzenie będzie działać tylko z tymi, CONDktóre są oceniane w czasie kompilacji. Nie będzie działać (tj. Kompilacja się nie powiedzie) z warunkami opartymi na wartościach w pamięci, takich jak wartości przypisane do zmiennych.

FredFredFredFred
źródło
4
Chociaż mogłoby to zadziałać, zwiększyłoby to również wymagania dotyczące pamięci.
sherrellbc
1
błąd: „static_assertion_INVALID_CHAR_SIZE” zdefiniowane, ale nieużywane [-Werror = unused-variable]
Alex
2

Jeśli używasz makra STATIC_ASSERT () z __LINE__, możliwe jest uniknięcie kolizji numerów linii między wpisem w pliku .c a innym wpisem w pliku nagłówkowym przez dołączenie __INCLUDE_LEVEL__.

Na przykład :

/* 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
źródło
1

Klasycznym sposobem jest użycie tablicy:

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

Działa, ponieważ jeśli twierdzenie jest prawdziwe, tablica ma rozmiar 1 i jest poprawne, ale jeśli jest fałszywe, rozmiar -1 powoduje błąd kompilacji.

Większość kompilatorów pokaże nazwę zmiennej i wskaże prawą część kodu, w której można zostawić ewentualne komentarze dotyczące potwierdzenia.

Paolo.Bolzoni
źródło
Umieszczenie tego w #define STATIC_ASSERT()makrze typu ogólnego i dostarczenie bardziej ogólnych przykładów i przykładowych danych wyjściowych kompilatora z przykładów ogólnych przy użyciu STATIC_ASSERT()dałoby o wiele więcej pozytywnych głosów i sprawiłoby, że ta technika miałaby większy sens.
Gabriel Staples
Nie zgadzam się. Kompilator widzi makra myśli i podaje bardziej zagmatwany komunikat.
Paolo.Bolzoni
1

Z Perla, a konkretnie perl.hlinia 3455 ( <assert.h>zawarta wcześniej):

/* 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

Jeśli static_assertjest dostępny (od <assert.h>), jest używany. W przeciwnym razie, jeśli warunek jest fałszywy, deklarowane jest pole bitowe o rozmiarze ujemnym, co powoduje niepowodzenie kompilacji.

STMT_START/ STMT_ENDto makra rozwijane odpowiednio do do/ while (0).

melpomene
źródło
1

Dlatego:

  1. _Static_assert() jest teraz zdefiniowany w gcc dla wszystkich wersji C i
  2. static_assert() jest zdefiniowany w C ++ 11 i nowszych wersjach

Dlatego następujące proste makro for STATIC_ASSERT()działa w:

  1. C ++:
    1. C ++ 11 ( g++ -std=c++11) lub nowszy
  2. DO:
    1. gcc -std=c90
    2. gcc -std=c99
    3. gcc -std=c11
    4. gcc (nie określono standardu)

Zdefiniuj STATIC_ASSERTw następujący sposób:

/* 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")

Teraz użyj:

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

Przykłady:

Testowane w Ubuntu przy użyciu gcc 4.8.4:

Przykład 1: dobry gccwynik (tj .: STATIC_ASSERT()kody działają, ale warunek był fałszywy, powodując asert w czasie kompilacji):

$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: W funkcji „main”
static_assert.c: 78: 38: błąd: statyczne potwierdzenie nie powiodło się: „(1> 2) nie powiodło się”
#define STATIC_ASSERT (test_for_true ) _Static_assert ((test_for_true), "(" #test_for_true ") nie powiodło się")
^
static_assert.c: 88: 5: uwaga: w rozwinięciu makra 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Przykład 2: dobry g++ -std=c++11wynik (tj .: STATIC_ASSERT()kody działają, ale warunek był fałszywy, powodując asert w czasie kompilacji):

$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c: In function 'int main ()'
static_assert.c: 74: 32: error: static assertion failed: (1> 2) nie powiodło się
#define _Static_assert static_assert / * static_assertjest częścią C ++ 11 lub nowszego * /
^
static_assert.c: 78: 38: uwaga: w rozszerzeniu makra '_Static_assert'
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") nie powiodło się")
^
static_assert.c: 88: 5: uwaga: podczas rozwijania makra 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Przykład 3: nieudane wyjście C ++ (tj .: kod assert w ogóle nie działa poprawnie, ponieważ używa wersji C ++ przed C ++ 11):

$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 88: 5: warning: identifier „static_assert” jest słowem kluczowym w C ++ 11 [-Wc ++ 0x-
Compatible ] STATIC_ASSERT (1> 2 );
^
static_assert.c: W funkcji 'int main ()'
static_assert.c: 78: 99: error: 'static_assert' nie zostało zadeklarowane w tym zakresie
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true " ) nie powiodło się ")
^
static_assert.c: 88: 5: uwaga: podczas rozwijania makra 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Pełne wyniki testu tutaj:

/*
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;
}

Związane z:

  1. Użyj static_assert, aby sprawdzić typy przekazane do makra [moja własna odpowiedź]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. Użyj static_assert, aby sprawdzić typy przekazane do makra
  3. Jak używać potwierdzenia statycznego w języku C, aby sprawdzić typy parametrów przekazanych do makra
Gabriel Staples
źródło
1
Dlaczego tak skomplikowane, skoro jest w nim static_assertmakro assert.h?
Do widzenia SE
@KamiKaze, Twoje pytanie mnie zaskoczyło, bo wygląda na to, że nie przeczytałeś mojej odpowiedzi? Drugi wiersz mojej odpowiedzi mówi wszystko: „static_assert () jest zdefiniowane w C ++ 11 i późniejszych”. Dlatego static_assert()w ogóle nie jest dostępny w C. Zobacz też: en.cppreference.com/w/cpp/language/static_assert --it shows there static_assert"(od C ++ 11)". Piękno mojej odpowiedzi polega na tym, że działa w C90 gcc i późniejszych, a także w każdym C ++ 11 i później, zamiast tylko w C ++ 11 i nowszych, jak static_assert(). Co jest skomplikowanego w mojej odpowiedzi? To tylko kilka #definesekund.
Gabriel Staples,
static_assertjest zdefiniowany w C od C11. Jest to makro, które rozwija się do _Static_assert. en.cppreference.com/w/c/error/static_assert . Dodatkowo, w przeciwieństwie do twojej odpowiedzi _Static_assertnie jest dostępna w c99 i c90 w gcc (tylko w gnu99 i gnu90). Jest to zgodne z normą. Zasadniczo wykonujesz dużo dodatkowej pracy, która przyniesie korzyści tylko wtedy, gdy zostanie skompilowana z gnu90 i gnu99, a faktyczny przypadek użycia będzie nieznacznie mały.
Do widzenia SE
> „Funkcja _Static_assert nie jest dostępna w c99 i c90 w gcc (tylko w gnu99 i gnu90)”. Rozumiem, co masz na myśli. Jest to rozszerzenie gcc, więc masz rację. > „Zasadniczo wykonujesz dużo dodatkowej pracy”. Nie zgadzam się; 2 niezwykle proste definicje to wcale nie „dużo” dodatkowej pracy. Biorąc to pod uwagę, teraz rozumiem, co masz na myśli. Nadal uważam, że to, co zrobiłem, jest użyteczne i dodaje wartości do całej wiedzy i odpowiedzi tutaj przedstawionych, więc nie sądzę, aby zasługiwało na negatywną opinię. Poza tym mój błąd, mówiąc „C90 i później” zamiast „gcc C90 i później” lub „g90 i później”, był tylko w moim komentarzu powyżej, a nie w mojej odpowiedzi.
Gabriel Staples
Ponieważ było to nieprawdziwe, głos przeciw był uzasadniony. Jeśli poprawisz błędne stwierdzenia, ponownie sprawdzę odpowiedź i mogę wycofać mój głos przeciw. Nadal dodawanie takiego kodu, jeśli nie jest to konieczne (więc jeśli nie pracujesz z gnu90 i gnu99), nie jest korzystne dla przejrzystości i dodaje więcej bałaganu. Jeśli masz przypadek, może być tego warte. Ale zastanawiam się nad rzadkością przypadków użycia, w których wymagana jest kompatybilność gnu99 / 90 i c ++ 11.
Do widzenia SE
0

Dla tych z Was, którzy chcą czegoś naprawdę prostego i przenośnego, ale nie mają dostępu do funkcji C ++ 11, napisałem właśnie to.
Używaj STATIC_ASSERTnormalnie (możesz napisać to dwukrotnie w tej samej funkcji, jeśli chcesz) i używaj GLOBAL_STATIC_ASSERTpoza funkcjami z unikalną frazą jako pierwszym parametrem.

#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;
}

Wyjaśnienie:
Najpierw sprawdza, czy masz prawdziwy assert, którego na pewno chciałbyś użyć, jeśli jest dostępny.
Jeśli tego nie zrobisz, to zapewnia, zdobywając swój predlód i dzieląc go samodzielnie. To robi dwie rzeczy.
Jeśli wynosi zero, id est, asercja się nie powiodła, spowoduje błąd dzielenia przez zero (arytmetyka jest wymuszona, ponieważ próbuje zadeklarować tablicę).
Jeśli nie jest zerem, normalizuje rozmiar tablicy do 1. Więc jeśli asercja przeszła pomyślnie, i tak nie chciałbyś, aby zakończyło się niepowodzeniem, ponieważ predykat został oceniony jako -1(nieprawidłowy) lub był 232442(ogromne marnowanie miejsca, IDK, jeśli zostałby zoptymalizowany).
Ponieważ STATIC_ASSERTjest zawinięty w nawiasy klamrowe, jest to blok, który określa zakres zmiennejassert, co oznacza, że ​​możesz to napisać wiele razy.
Rzuca go również na void, co jest znanym sposobem na pozbycie się unused variableostrzeżeń.
Dla GLOBAL_STATIC_ASSERT, zamiast w bloku kodu, generuje nazw. Przestrzenie nazw są dozwolone poza funkcjami. uniqueIdentyfikator wymagane jest, aby zatrzymać wszelkie sprzeczne definicje jeśli używasz ten jeden więcej niż raz.


Pracował dla mnie na GCC i VS'12 C ++

Hashbrown
źródło
2
W C.
martinkunev
ah, ups, źle odczytałem pytanie. Wygląda na to, że przyszedłem tutaj, szukając odpowiedzi na C ++ (patrząc na ostatnią linię mojej odpowiedzi), więc zostawię to tutaj na wypadek, gdyby inni zrobili to samo
Hashbrown
0

Działa to z ustawioną opcją „usuń nieużywane”. Mogę użyć jednej funkcji globalnej do sprawdzenia parametrów globalnych.

//
#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
źródło
1
Jeśli to w ogóle zadziała, zrobi to tylko w źródle pliku wykonywalnego.
Coder
0

To zadziałało dla niektórych starych gcc. Przepraszam, że zapomniałem jaka to była wersja:

#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
sójka
źródło