Assertion statique en C

86

Quelle est la meilleure façon d'obtenir des assertions statiques au moment de la compilation en C (et non en C ++), avec un accent particulier sur GCC?

Matt Joiner
la source

Réponses:

90

La norme C11 ajoute le _Static_assertmot - clé.

Ceci est implémenté depuis gcc-4.6 :

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

Le premier emplacement doit être une expression constante intégrale. Le second slot est un littéral de chaîne constante qui peut être long ( _Static_assert(0, L"assertion of doom!")).

Je dois noter que cela est également implémenté dans les versions récentes de clang.

emsr
la source
4
[... semble être implémenté par gcc, par clang ...] Vous pouvez être plus affirmatif que ;-) _Static_assertfait partie du standard C11 et tout compilateur qui supporte C11 l'aura.
PP
1
Cela peut-il être utilisé au niveau du fichier (en dehors de toute fonction)? Parce que je reçois error: expected declaration specifiers or '...' before 'sizeof'pour la ligne static_assert( sizeof(int) == sizeof(long int), "Error!); (j'utilise C pas C ++ d'ailleurs)
user10607
@ user10607 Je suis surpris que cela ne fonctionne pas. Attendez, il vous manque un guillemet à la fin de votre chaîne d'erreur. Mettez ça dedans et revenez. Cela fonctionne pour moi sur gcc-4.9: _Static_assert( sizeof(int) == sizeof(long int), "Error!");Sur mon macine, j'obtiens l'erreur.
emsr
J'ai gcc 4.8.2 sur Ubuntu. La citation manquante était une faute de frappe (je l'avais dans le code). Il s'agit de la première ligne d'un fichier après quelques en-têtes inclus. Le compilateur me donne exactement deux erreurs identiques: error: expected declaration specifiers or '...' before 'sizeof'AND error: expected declaration specifiers or '...' before string constant(il fait référence à la "Error!"chaîne) (aussi: je compile avec -std = c11. Lorsque vous placez la déclaration dans une fonction, tout fonctionne bien (échoue et réussit comme prévu))
user10607
2
@ user10607 J'ai également dû spécifier -std = gnu11 sur la ligne de commande. Je suis vraiment surpris qu'il y ait une différence entre 4,8 et 4,8. J'ai une source avec une seule ligne. J'ai également utilisé le standard C et _Static_assertnon le C ++ static_assert. Vous devez `#include <assert.h> pour obtenir la macro static_assert.
emsr
93

Cela fonctionne dans la portée des fonctions et des non-fonctions (mais pas à l'intérieur des structures, des unions).

#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. Si l'assertion de temps de compilation n'a pas pu être mise en correspondance, alors un message presque intelligible est généré par GCC sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative

  2. La macro pourrait ou devrait être modifiée pour générer un nom unique pour le typedef (c'est- __LINE__à- dire concaténer à la fin du static_assert_...nom)

  3. Au lieu d'un ternaire, cela pourrait également être utilisé, ce #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]qui fonctionne même sur le compilateur rusty olde cc65 (pour le processeur 6502).

MISE À JOUR: Par souci d'exhaustivité, voici la version avec__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: code spécifique à GCC

GCC 4.3 (je suppose) a introduit les attributs de fonction "erreur" et "avertissement". Si un appel à une fonction avec cet attribut n'a pas pu être éliminé par l'élimination du code mort (ou d'autres mesures), une erreur ou un avertissement est généré. Cela peut être utilisé pour faire des assertions de compilation avec des descriptions d'échec définies par l'utilisateur. Il reste à déterminer comment ils peuvent être utilisés dans la portée de l'espace de noms sans recourir à une fonction factice:

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

Et voici à quoi ça ressemble:

$ 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
Mainframe nordique
la source
1
Dans Visual Studio, il dit simplement "
Indice
Nordic Mainframe - l'option 3 de votre réponse ne fonctionne pas sur clang.
Elazar
1
Concernant la dernière solution (spécifique à GCC 4.3 +): C'est très puissant, car il peut vérifier tout ce que l'optimiseur peut comprendre, mais cela échoue si l'optimisation n'est pas activée. Le niveau d'optimisation minimal ( -Og) peut souvent être suffisant pour que cela fonctionne, cependant, et ne devrait pas interférer avec le débogage. On peut envisager de faire de l'assertion statique une assertion no-op ou d'exécution si __OPTIMIZE__(et __GNUC__) n'est pas défini.
Søren Løvborg
Dans l'extrait de code avec la version LINE (UPDATE: Par souci d'exhaustivité, voici la version avec `LINE), lors de la compilation, il y a des erreurs à la ligne (STATIC_ASSERT (X, static_assertion_at_line _ ## L)), qui peuvent être corrigées en ajoutant un autre niveau comme ci-dessous: #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); #define COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, "" Assertion à: ## L "");
dimanche
J'utilise quelque chose de similaire à la __LINE__version de gcc 4.1.1 ... avec parfois des ennuis lorsque deux en-têtes différents en ont un sur la même ligne numérotée!
MM
10

cl

Je sais que la question mentionne explicitement gcc, mais juste pour être complet, voici un ajustement pour les compilateurs Microsoft.

L'utilisation du tableau de taille négative typedef ne persuade pas cl de cracher une erreur décente. Cela dit simplement error C2118: negative subscript. Un champ de bits de largeur nulle est meilleur à cet égard. Comme cela implique de taper une structure, nous devons vraiment utiliser des noms de type uniques. __LINE__ne coupe pas la moutarde - il est possible d'avoir un COMPILE_TIME_ASSERT()sur la même ligne dans un en-tête et un fichier source, et votre compilation s'arrêtera. __COUNTER__vient à la rescousse (et il est dans gcc depuis 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__)

Maintenant

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

sous cldonne:

erreur C2149: 'static_assertion_failed_use_another_compiler_luke': le champ de bits nommé ne peut pas avoir une largeur nulle

Gcc donne également un message intelligible:

erreur: largeur zéro pour le champ de bits 'static_assertion_failed_use_another_compiler_luke'

bobbogo
la source
4

De Wikipedia :

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

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
Tyler
la source
15
Ce serait mieux si vous vous connectez
Matt Joiner
Cela ne fonctionne pas dans gcc 4.6 - il dit que "l'étiquette de cas ne se réduit pas à une constante entière". Il a un point.
Liosan
vous avez probablement tous les deux déménagé maintenant, mais j'ai fini par écrire le mien (voir ma réponse ). J'ai utilisé votre lien @MattJoiner pour m'aider
Hashbrown
Et si cela vous dérange, faites-moi savoir si cela fonctionne pour vous, @Liosan. Je viens juste de commencer à me plonger dans le C ++, donc je suis arrivé en retard à la fête
Hashbrown
Quant à Visual C ++, il est intégré à static_assert depuis la version 2010 et fonctionne à la fois en modes c ++ et c. Cependant, il n'a pas le c99 _Static_assert intégré.
ddbug
3

Je ne recommanderais PAS d' utiliser la solution en utilisant un typedef:

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

La déclaration de tableau avec le typedefmot clé n'est PAS garantie d'être évaluée au moment de la compilation. Par exemple, le code suivant dans la portée de bloc sera compilé:

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

Je recommanderais plutôt ceci (sur C99):

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

En raison du staticmot - clé, le tableau sera défini au moment de la compilation. Notez que cette assertion fonctionnera uniquement avec ceux CONDqui sont évalués au moment de la compilation. Cela ne fonctionnera pas (c'est-à-dire que la compilation échouera) avec des conditions basées sur des valeurs en mémoire, telles que des valeurs affectées à des variables.

FredFredFredFred
la source
4
Bien que cela fonctionne, cela augmenterait également vos besoins en mémoire.
sherrellbc
1
erreur: 'static_assertion_INVALID_CHAR_SIZE' défini mais non utilisé [-Werror = unused-variable]
Alex
2

Si vous utilisez la macro STATIC_ASSERT () avec __LINE__, il est possible d'éviter les conflits de numéros de ligne entre une entrée dans un fichier .c et une entrée différente dans un fichier d'en-tête en incluant __INCLUDE_LEVEL__.

Par exemple :

/* 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
la source
1

La méthode classique utilise un tableau:

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

Cela fonctionne car si l'assertion est vraie, le tableau a la taille 1 et elle est valide, mais si elle est fausse, la taille -1 donne une erreur de compilation.

La plupart des compilateurs afficheront le nom de la variable et pointeront vers la partie droite du code où vous pourrez laisser d'éventuels commentaires sur l'assertion.

Paolo.Bolzoni
la source
Emballer cela dans une #define STATIC_ASSERT()macro de type générique et fournir des exemples plus génériques et des exemples de sortie de compilateur à partir de vos exemples génériques en utilisant STATIC_ASSERT()vous donnerait beaucoup plus de votes positifs et rendrait cette technique plus logique, je pense.
Gabriel Staples
Je ne suis pas d'accord. Le compilateur voit des macros de pensée et donne un message plus déroutant.
Paolo.Bolzoni
1

Depuis Perl, en particulier la perl.hligne 3455 ( <assert.h>incluse au préalable):

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

Si static_assertest disponible (de <assert.h>), il est utilisé. Sinon, si la condition est fausse, un champ de bits avec une taille négative est déclaré, ce qui provoque l'échec de la compilation.

STMT_START/ STMT_ENDsont des macros s'étendant vers do/ while (0), respectivement.

melpomène
la source
1

Car:

  1. _Static_assert() est maintenant défini dans gcc pour toutes les versions de C, et
  2. static_assert() est défini dans C ++ 11 et versions ultérieures

La macro simple suivante pour STATIC_ASSERT()fonctionne donc dans:

  1. C ++:
    1. C ++ 11 ( g++ -std=c++11) ou version ultérieure
  2. C:
    1. gcc -std=c90
    2. gcc -std=c99
    3. gcc -std=c11
    4. gcc (pas de std spécifié)

Définissez STATIC_ASSERTcomme suit:

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

Maintenant, utilisez-le:

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

Exemples:

Testé sous Ubuntu avec gcc 4.8.4:

Exemple 1: bonne gccsortie (c'est-à-dire: les STATIC_ASSERT()codes fonctionnent, mais la condition était fausse, provoquant une assertion à la compilation):

$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: Dans la fonction 'main'
static_assert.c: 78: 38: erreur: échec de l'assertion statique: "(1> 2) échoué"
#define STATIC_ASSERT (test_for_true ) _Static_assert ((test_for_true), "(" #test_for_true ") failed")
^
static_assert.c: 88: 5: note: dans le développement de la macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Exemple 2: bonne g++ -std=c++11sortie (ie: les STATIC_ASSERT()codes fonctionnent, mais la condition était fausse, provoquant une assertion à la compilation):

$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c: Dans la fonction 'int main ()'
static_assert.c: 74: 32: erreur: l'assertion statique a échoué: (1> 2) échoué
#define _Static_assert static_assert / * static_assertfait partie de C ++ 11 ou version ultérieure * /
^
static_assert.c: 78: 38: note: dans le développement de la macro '_Static_assert'
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") failed")
^
static_assert.c: 88: 5: note: dans le développement de la macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Exemple 3: échec de la sortie C ++ (c'est-à-dire: le code d'assertion ne fonctionne pas du tout correctement, car il utilise une version de C ++ antérieure à C ++ 11):

$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 88: 5: avertissement: l'identificateur 'static_assert' est un mot-clé en C ++ 11 [-Wc ++ 0x-compat]
STATIC_ASSERT (1> 2 );
^
static_assert.c: Dans la fonction 'int main ()'
static_assert.c: 78: 99: erreur: 'static_assert' n'a pas été déclaré dans cette portée
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true " ) a échoué ")
^
static_assert.c: 88: 5: note: dans le développement de la macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Résultats complets du test ici:

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

En relation:

  1. Utilisez static_assert pour vérifier les types passés à la macro [ma propre réponse]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. Utilisez static_assert pour vérifier les types passés à la macro
  3. Comment utiliser l'assertion statique en C pour vérifier les types de paramètres passés à une macro
Gabriel Staples
la source
1
Pourquoi si compliqué, quand il y a une static_assertmacro dedans assert.h?
Au revoir le
@KamiKaze, je suis surpris par votre question, car il semble que vous n'ayez pas lu ma réponse? La deuxième ligne de ma réponse dit tout: "static_assert () est défini dans C ++ 11 et plus tard". Par conséquent, static_assert()n'est pas du tout disponible en C. Voir aussi ici: en.cppreference.com/w/cpp/language/static_assert - cela montre qu'il static_assertexiste "(depuis C ++ 11)". La beauté de ma réponse est que cela fonctionne dans le C90 de gcc et les versions ultérieures, ainsi que dans tout C ++ 11 et versions ultérieures, au lieu de seulement dans C ++ 11 et versions ultérieures, comme static_assert(). Aussi, qu'est-ce qui est compliqué dans ma réponse? Ce n'est que quelques #defines.
Gabriel Staples
static_assertest défini en C depuis C11. C'est une macro qui se développe _Static_assert. en.cppreference.com/w/c/error/static_assert . De plus, le contraste avec votre réponse _Static_assertn'est pas disponible dans c99 et c90 dans gcc (uniquement dans gnu99 et gnu90). Ceci est conforme à la norme. En gros, vous faites beaucoup de travail supplémentaire, cela n'apporte des avantages que si compilé avec gnu90 et gnu99 et qui rend le cas d'utilisation réel insignifiant.
Au revoir le
> "_Static_assert n'est pas disponible dans c99 et c90 dans gcc (uniquement dans gnu99 et gnu90)". Je vois ce que tu veux dire. C'est une extension gcc donc vous avez raison. > "En gros, vous faites beaucoup de travail supplémentaire". Je ne suis pas d'accord; 2 définitions extrêmement simples ne représente en aucun cas «beaucoup» de travail supplémentaire. Cela étant dit, je vois ce que vous voulez dire maintenant. Je pense toujours que ce que j'ai fait est utile et ajoute de la valeur à l'ensemble des connaissances et des réponses présentées ici, donc je ne pense pas que cela mérite un vote défavorable. De plus, mon erreur en disant "C90 et versions ultérieures" au lieu de "gcc C90 et versions ultérieures", ou "g90 et versions ultérieures", était uniquement dans mon commentaire ci-dessus, pas dans ma réponse.
Gabriel Staples
Comme c'était faux en fait, un vote défavorable était justifié. Si vous corrigez les mauvaises déclarations, je vérifierai à nouveau la réponse et je pourrai retirer mon vote défavorable. L'ajout d'un tel code s'il n'est pas nécessaire (donc si vous ne travaillez pas avec gnu90 et gnu99) n'est pas bénéfique pour la clarté et ajoute plus de fouillis. Si vous avez le cas d'utilisation, cela en vaut la peine. Mais je m'interroge sur la rareté du cas d'utilisation où la compabilité gnu99 / 90 et c ++ 11 est requise.
Au revoir le
0

Pour ceux d'entre vous qui veulent quelque chose de vraiment basique et portable mais qui n'ont pas accès aux fonctionnalités de C ++ 11, j'ai juste écrit la chose.
Utilisez STATIC_ASSERTnormalement (vous pouvez l'écrire deux fois dans la même fonction si vous le souhaitez) et utilisez en GLOBAL_STATIC_ASSERTdehors des fonctions avec une phrase unique comme premier paramètre.

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

Explication:
Tout d'abord, il vérifie si vous avez la véritable assertion, que vous voudriez certainement utiliser si elle est disponible.
Si vous ne le faites pas, il affirme en obtenant votre predglace et en la divisant par elle-même. Cela fait deux choses.
Si c'est zéro, id est, l'assertion a échoué, cela provoquera une erreur de division par zéro (l'arithmétique est forcée car elle essaie de déclarer un tableau).
S'il n'est pas égal à zéro, il normalise la taille du tableau à 1. Donc, si l'assertion est réussie, vous ne voudriez pas qu'elle échoue de toute façon parce que votre prédicat est évalué à -1(invalide), ou à 232442(perte massive d'espace, IDK s'il était optimisé).
Car STATIC_ASSERTil est entouré d'accolades, cela en fait un bloc, qui couvre la variableassert, ce qui signifie que vous pouvez l'écrire plusieurs fois.
Il le lance également void, ce qui est un moyen connu de se débarrasser des unused variableavertissements.
Car GLOBAL_STATIC_ASSERT, au lieu d'être dans un bloc de code, il génère un espace de noms. Les espaces de noms sont autorisés en dehors des fonctions. Un uniqueidentifiant est requis pour arrêter toute définition en conflit si vous l'utilisez plusieurs fois.


A travaillé pour moi sur GCC et VS'12 C ++

Hashbrown
la source
2
Il n'y a pas d'espaces de noms dans C.
martinkunev
ah, oups, mal interprété la question. On dirait que je suis venu ici pour chercher une réponse à C ++ de toute façon (en regardant la dernière ligne de ma réponse), donc je vais la laisser ici au cas où d'autres feraient de même
Hashbrown
0

Cela fonctionne, avec le jeu d'options "supprimer inutilisé". Je peux utiliser une fonction globale pour vérifier les paramètres globaux.

//
#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
la source
1
Si cela fonctionne du tout, il ne le ferait que dans la source d'un exécutable.
Codeur
0

Cela a fonctionné pour certains vieux gcc. Désolé d'avoir oublié de quelle version il s'agissait:

#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
geai
la source