Statique affirmer en C
Quelle est la meilleure façon de parvenir à la compilation statique affirme en C (pas C++), avec un accent particulier sur GCC?
- Voici un retardataire réponse de la mienne qui fonctionne en C++11 ou une version ultérieure et avec toutes les modernes gcc le compilateur C versions: stackoverflow.com/a/54993033/4561887
Vous devez vous connecter pour publier un commentaire.
C11 standard ajoute le
_Static_assert
mot-clé.C'est mis en œuvre depuis gcc-4.6:
La première machine à sous doit être une expression constante. Le second logement est une constante chaîne de caractères littérale qui peut être long (
_Static_assert(0, L"assertion of doom!")
).Je tiens à noter que c'est également mise en œuvre dans les versions récentes de clang.
_Static_assert
fait partie de la norme C11 et un compilateur qui prend en charge C11, aurez.error: expected declaration specifiers or '...' before 'sizeof'
pour la lignestatic_assert( sizeof(int) == sizeof(long int), "Error!);
(je suis en utilisant C pas du C++ par la voie)_Static_assert( sizeof(int) == sizeof(long int), "Error!");
Sur ma macine je reçois le message d'erreur.error: expected declaration specifiers or '...' before 'sizeof'
ETerror: expected declaration specifiers or '...' before string constant
(il se réfère à la"Error!"
string) (aussi: je suis de la compilation avec -std=c11. Lors de la mise de la déclaration à l'intérieur d'une fonction tous fonctionne bien (échoue et réussit comme prévu))_Static_assert
pas le C++ishstatic_assert
. Vous devez " #include <assert.h> pour obtenir le static_assert macro.static_assert
comme valable même si j'ai oublié le <assert.h>. Noob piège :))_Static_assert()
en C et C++11 eststatic_assert
afin qu'il fonctionne avecgcc
,gcc -std=c90
,gcc -std=c99
,gcc -std=c11
, etg++ -std=c++11
, etc: stackoverflow.com/a/54993033/4561887Cela fonctionne dans la fonction et les non-portée de la fonction (mais pas à l'intérieur des structures,des syndicats).
Si le moment de la compilation affirmation n'a pas pu être appariés, puis une presque intelligible message est généré par GCC
sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative
La macro qui pourrait ou devrait être modifié pour générer un nom unique pour la définition de type (c'est à dire concaténer
__LINE__
à la fin de lastatic_assert_...
nom)Au lieu d'un ternaire, ce pourrait être aussi bien utilisé
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]
qui se passe à travailler encore sur le rusty olde cc65 (pour le processeur 6502) compilateur.Mise à JOUR:
Par souci d'exhaustivité, voici la version avec
__LINE__
UPDATE2: GCC code spécifique
GCC 4.3 (je suppose) a introduit le "erreur" et "avertissement" de la fonction d'attributs. Si un appel à une fonction avec cet attribut ne peut être éliminée grâce à l'élimination du code mort (ou d'autres mesures), puis une erreur ou un avertissement est généré. Ceci peut être utilisé pour faire de la compilation affirme avec l'utilisateur défini par l'échec des descriptions. Il reste à déterminer comment ils peuvent être utilisés dans l'espace de noms de la portée sans avoir recours à un mannequin de la fonction:
Et c'est à quoi il ressemble:
-Og
) peut souvent être assez pour que cela fonctionne, cependant, et ne devrait pas interférer avec le débogage. On peut envisager de faire de la statique affirmer un no-op ou de l'exécution d'affirmer si__OPTIMIZE__
(et__GNUC__
) n'est pas défini.__LINE__
version de gcc 4.1.1 ... avec parfois de l'agacement lorsque deux en-têtes d'arriver à en avoir une sur le même numéro de ligne!typedef
version fonctionne avec tous les compilateurs. Toutefois, si le compilateur prend en charge__attribute__((unused))
, il est commode d'ajouter, de peur d'être embêté par-Wunused-local-typedef
.cl
Je sais que la question mentionne explicitement la gcc, mais juste pour être complet, voici un tweak pour les compilateurs Microsoft.
À l'aide de l'négativement tableau de taille typedef n'a pas réussi à persuader cl à cracher un décent erreur. Il dit seulement
error C2118: negative subscript
. Un zéro-largeur de champ de bits tarifs mieux à cet égard. Puisqu'il s'agit d'typedeffing un struct, nous avons vraiment besoin d'utiliser des noms de types uniques.__LINE__
ne pas couper la moutarde — il est possible d'avoir unCOMPILE_TIME_ASSERT()
sur la même ligne dans un en-tête et d'un fichier source, et votre compilation de casser.__COUNTER__
vient à la rescousse (et il a été dans gcc depuis 4.3).Maintenant
sous
cl
donne:Gcc donne également un message intelligible:
De Wikipédia:
Je PAS recommandons l'utilisation de la solution à l'aide d'un
typedef
:La déclaration de tableau avec
typedef
mot clé n'est PAS garanti d'être évalués au moment de la compilation. Par exemple, le code suivant dans le bloc de portée de compiler:Je recommanderais ceci à la place (sur C99):
En raison de la
static
mot-clé, le tableau sera défini au moment de la compilation. Notez que cette assertion ne fonctionne qu'avecCOND
qui sont évalués au moment de la compilation. Il ne fonctionnera pas (par exemple, la compilation échouera) avec des conditions qui sont fondés sur des valeurs en mémoire, comme les valeurs affectées aux variables.Si à l'aide de la STATIC_ASSERT() macro avec
__LINE__
, il est possible d'éviter la ligne de nombre d'affrontements entre une entrée dans une .c fichier et une autre entrée dans un fichier d'en-tête, y compris__INCLUDE_LEVEL__
.Par exemple :
La méthode la plus classique est l'utilisation d'un tableau:
Cela fonctionne, car si l'affirmation est vraie que le tableau a de taille 1 et il est valide, mais si elle est fausse la taille de -1 donne une erreur de compilation.
La plupart des compilateurs affichera le nom de la variable et le point à droite de la partie du code où vous pouvez laisser les commentaires éventuels sur l'assertion.
#define STATIC_ASSERT()
type de macro et de fournir plus de génériques, des exemples et exemples de sortie du compilateur à partir de votre générique des exemples d'utilisation deSTATIC_ASSERT()
serait de vous donner un beaucoup plus upvotes et de rendre cette technique plus de sens je pense.Parce que:
_Static_assert()
est maintenant défini dans gcc pour toutes les versions de C, etstatic_assert()
est défini en C++11 et plus tardLa macro suivante simple pour
STATIC_ASSERT()
fonctionne donc en:g++ -std=c++11
) ou plus tardgcc -std=c90
gcc -std=c99
gcc -std=c11
gcc
(pas de mst spécifié)Définir
STATIC_ASSERT
comme suit:Maintenant l'utiliser:
Exemples:
Testé sous Ubuntu à l'aide de gcc 4.8.4:
Exemple 1: bonne
gcc
de sortie (ie: leSTATIC_ASSERT()
codes fonctionne, mais que la condition est fausse, en provoquant un moment de la compilation assert):Exemple 2: bonne
g++ -std=c++11
de sortie (ie: leSTATIC_ASSERT()
codes fonctionne, mais que la condition est fausse, en provoquant un moment de la compilation assert):Exemple 3: échoué sortie C++ (c'est à dire: l'assertion code ne fonctionne pas correctement à tous, puisque c'est à l'aide d'une version de C++ avant C++11):
Test complet ici:
Pour ceux qui veulent quelque chose de vraiment basique et portable, mais n'ont pas accès à C++11 caractéristiques, j'ai écrit un peu la chose.
Utilisation
STATIC_ASSERT
normalement (vous pouvez l'écrire deux fois dans la même fonction si vous voulez) et l'utilisationGLOBAL_STATIC_ASSERT
à l'extérieur de fonctions avec une phrase unique en tant que premier paramètre.Explication:
D'abord, il vérifie si vous avez la vraie affirmer, que vous voulez absolument utiliser si elle est disponible.
Si vous n'avez pas il affirme par l'obtention de votre
pred
icate, et en la divisant par lui-même. Cela ne signifie deux choses.Si c'est le zéro, id est, l'assertion a échoué, il va provoquer une erreur de division par zéro (l'arithmétique est forcé, parce qu'elle essaye de déclarer un tableau).
Si il n'est pas nul, il normalise la taille de la matrice à
1
. Donc, si l'affirmation passé, vous ne voulez pas échouer de toute façon parce que vos prédicat est évalué à-1
(invalide), ou être232442
(énorme gaspillage de l'espace, IDK si il serait optimisé out).Pour
STATIC_ASSERT
il est enveloppé dans des accolades, ce qui en fait un bloc, qui étendues de la variableassert
, ce qui signifie que vous pouvez l'écrire à plusieurs reprises.Il jette aussi à
void
, qui est un moyen de se débarrasser deunused variable
avertissements.Pour
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. Ununique
identifiant est nécessaire d'arrêter toutes les définitions contradictoires si vous utilisez plus d'une fois.A fonctionné pour moi sur GCC et VS'12 C++
Cela fonctionne, avec "supprimer les" option. Je peut utiliser une fonction globale de vérifier les paramètres globaux.
Cela a fonctionné pour certains vieux gcc. Désolé que j'ai oublié quelle version c'était:
De Perl, spécifiquement
perl.h
de la ligne 3455 (<assert.h>
est inclus à l'avance):Si
static_assert
est disponible (à partir de<assert.h>
), il est utilisé. Sinon, si la condition est fausse, un peu de champ avec une taille négative est déclarée, ce qui provoque la compilation échoue.STMT_START
/STMT_END
sont des macros expansion dedo
/while (0)
, respectivement.