C / C++: Comment utiliser le do-while( 0); construire sans avertissement de compilateur comme le T4127?
je suis souvent utiliser do-while(0) construire dans mon #définit, pour les raisons décrites dans cette réponse . J'essaie aussi d'utiliser un niveau d'avertissement aussi élevé que possible du compilateur pour détecter plus de problèmes potentiels et rendre mon code plus robuste et multiplate-forme. Donc j'utilise typiquement -Wall
avec gcc et /Wall
avec MSVC.
malheureusement MSVC se plaindre de do-while (0) construire:
foo.c(36) : warning C4127: conditional expression is constant
quoi dois-je faire à propos de cet avertissement?
juste le désactiver globalement pour tous les fichiers? Il ne semble pas être une bonne idée pour moi.
20 réponses
résumé: cet avertissement (T4127) dans ce cas particulier est un bogue de compilateur subtil. Hésitez pas à le désactiver.
en profondeur:
il a été conçu pour attraper des situations où l'expression logique évalue à une constante dans des situations non évidentes (telles que, if(a==a && a!=a)
, et d'une certaine façon, il a tourné while(true)
et d'autres constructions utiles en non valide.
Microsoft recommande d'utiliser for(;;)
pour boucle infinie si vous voulez cet avertissement, et il n'y a pas de solution pour votre cas. C'est l'un des rares avertissements de niveau 4 que les conventions de développement de mon entreprise permettent de désactiver.
peut - être que votre code a besoin d'autres hiboux :
do { stuff(); } while (0,0)
ou moins photogénique mais aussi moins annonciatrice:
do { stuff(); } while ((void)0,0)
Comme Michael Bavure dans Carl Smotricz ' réponse , pour Visual Studio 2008+ vous pouvez utiliser __pragma :
#define MYMACRO(f,g) \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
do { f; g; } while (0) \
__pragma(warning(pop))
vous pouvez le mettre sur une ligne (sans les \
s) si vous préférez que les macros soient illisibles.
j'ai un modèle que j'ai basé sur une réponse ici et il fonctionne sur clang, gcc & MSVC. Je poste ici dans l'espoir qu'il sera utile pour les autres et parce que les réponses ici m'a aidé à formuler.
#ifdef WIN32
# define ONCE __pragma( warning(push) ) \
__pragma( warning(disable:4127) ) \
while( 0 ) \
__pragma( warning(pop) )
#else
# define ONCE while( 0 )
#endif
et je l'utilise comme ceci:
do {
// Some stuff
} ONCE;
vous pouvez l'utiliser dans les macros aussi:
void SomeLogImpl( const char* filename, int line, ... );
#ifdef NDEBUG
# define LOG( ... )
#else
# define LOG( ... ) do { \
SomeLogImpl( __FILE__, __LINE__, __VA_ARGS__ ); \
} ONCE
#endif
cela fonctionne aussi pour le cas indiqué ci-dessus, si F utilise 'ONCE' dans une fonction:
#define F( x ) do { f(x); } ONCE
...
if (a==b) F(bar); else someFunc();
Edit: des années plus tard, je me rends compte que j'ai oublié d'ajouter le modèle que j'ai en fait écrit cette macro pour-le" switch-like-a-goto "modèle:
do {
begin_some_operation();
if( something_is_wrong ) {
break;
}
continue_big_operation();
if( another_failure_cond ) {
break;
}
finish_big_operation();
return SUCCESS;
} ONCE;
cleanup_the_mess();
return FAILURE;
cela vous donne une construction try/finally-ish qui est plus structurée qu'un Goto crufty à votre code de nettoyage et de retour. En utilisant cette macro ONCE au lieu de while(0) Shut VS up.
en utilisant des versions plus récentes du compilateur MS, vous pouvez utiliser la suppression d'avertissement:
#define MY_MACRO(stuff) \
do { \
stuff \
__pragma(warning(suppress:4127)) \
} while(0)
, Vous pouvez aussi pousser/désactiver/pop, mais supprimer est beaucoup plus pratique mécanisme.
Voici une autre approche possible, qui évite le C4127, C4548 et C6319 (avertissement d'analyse de code VS2013), et ne nécessite pas de macros ou de pragmas:
static const struct {
inline operator bool() const { return false; }
} false_value;
do {
// ...
} while (false_value);
cela optimise loin, et compile sans avertissements dans GCC 4.9.2 et VS2013. En pratique, il pourrait aller dans un espace de noms.
l'avertissement est dû au while(false)
. Cette site donne un exemple de la façon de contourner ce problème. Exemple du site (vous devrez le retravailler pour votre code):
#define MULTI_LINE_MACRO_BEGIN do {
#define MULTI_LINE_MACRO_END \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
} while(0) \
__pragma(warning(pop))
#define MULTI_LINE_MACRO \
MULTI_LINE_MACRO_BEGIN \
std::printf("Hello "); \
std::printf("world!\n"); \
MULTI_LINE_MACRO_END
il suffit d'insérer votre code entre le début et la fin.
ce bug du compilateur a été corrigé dans la mise à jour 1 de Visual Studio 2015, même si le publie des notes ne le mentionne pas.
le bug a été expliqué dans l'une des réponses précédentes cependant:
résumé: cet avertissement (T4127) dans ce cas particulier est un bogue de compilateur subtil. Hésitez pas à le désactiver.
il a été conçu pour attraper des situations où l'expression logique évalue à une constante dans situations non évidentes (comme, si(a = = a && a!=a), et d'une manière ou d'une autre, il s'est transformé alors que(true) et d'Autres constructions utiles en invalide.
vous pouvez utiliser
do {
// Anything you like
} WHILE_FALSE;
et définir plus tôt WHILE_FALSE
macro comme suit:
#define WHILE_FALSE \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
while(false) \
__pragma(warning(pop))
vérifié sur MSVC++2013.
Ce "while(0)" s'étoffe d'un hack et vient de tourner autour de vous mordre.
votre compilateur offre-t-il #pragma
s pour désactiver sélectivement et localement les messages d'erreur spécifiques? Si tel est le cas, il pourrait s'agir d'une solution raisonnable.
#define STUFF for (bool b = true; b;) do {f(); g(); b = false;} while (b)
?
#define STUFF for (;;) {f(); g(); break;}
?
vous pouvez utiliser comma operator au lieu de do-while(0) construct pour la macro multi-statements à utiliser dans les expressions. Donc au lieu de:
#define FOO(...) do { Statement1; Statement2; Statement3; } while(0)
utiliser:
#define FOO(...) (Statement1, Statement2, Statement3)
cela fonctionne indépendamment de la plate-forme et permet d'éviter l'avertissement du compilateur (même si le niveau d'avertissement le plus élevé est sélectionné). Notez que dans la virgule contenant macro (second FOO) le résultat de la dernière instruction (Statement3) serait le résultat de macro entière.
je dois dire que je ne me suis jamais embêté avec le do..tandis que construire dans les macros. Tout le code dans Mes macros est lui-même inclus dans les accolades, mais sans le do -..alors. Par exemple:
#define F(x) \
{ \
x++; \
} \
int main() {
int a = 1;
F(a);
printf( "%d\n", a );
}
en outre, ma propre norme de codage (et la pratique informelle depuis des années) a été de faire tous les blocs, où qu'ils se produisent être enfermés dans des entretoises, qui élimine également plus ou moins le problème.
il y a une solution mais elle ajoutera plus de cycles à votre code. N'utilisez pas de valeur explicite dans la condition while.
vous pouvez le faire comme ceci:
fichier1.h
extern const int I_am_a_zero;
#define MY_MACRO(foo,bar) \
do \
{ \
} \
while(I_am_a_zero);
la variable I_am_a_zero doit être définie dans certains .c fichier.
de toute façon cet avertissement n'apparaît pas dans GCC:)
voir cette question connexe .
Vous pouvez utiliser #pragma warning:
- enregistrer l'état
- désactiver l'avertissement
- écrire le code fautif
- retourner l'avertissement à leur état antérieur
(vous avez besoin d'un # devant les pragmas, mais c'est aussi un moment difficile de traiter avec eux et de mise en forme en même temps)
#pragma warning( push )
#pragma warning( disable: 4127 )
// Your code
#pragma warning( pop )
vous voulez pousser / pop les Avertissements plutôt que de désactiver/activer parce que vous ne voulez pas interférer avec les arguments en ligne de commande qui pourraient être choisis pour activer/désactiver les Avertissements (quelqu'un peut utiliser la ligne de commande pour désactiver l'avertissement, vous ne voulez pas le forcer à revenir... le code ci-dessus traite de la).
c'est mieux que d'éteindre l'avertissement globalement car vous pouvez le contrôler juste pour la partie que vous voulez. Aussi, vous pouvez le faire partie de la macro.
Eh bien, pour moi, les travaux suivants sans le C4127 avertissement:
#define ALWAYS_TRUE(zzsome) ((##zzsome)==(##zzsome))
void foo()
{
int a = 0;
while( ALWAYS_TRUE(a) )
{
}
}
bien sûr, les compilateurs sont intelligents et zzsome ne devrait pas être une constante
cela désactivera l'avertissement et le compilateur pourra encore optimiser le code:
static inline bool to_bool(const bool v) { return v; }
if (to_bool(0)) { // no warning here
dead_code(); // will be compiled out (by most compilers)
}
do { something(); } while(to_bool(0)); // no extra code generated
j'ai trouvé que c'était la version la plus courte
do {
// ...
}
while (([]() { return 0; })()) /* workaround for MSVC warning C4172 : conditional expression is constant */
N'a pas vérifié s'il est optimisé par le compilateur, mais je suppose que c'est le cas.
vous pouvez utiliser for
boucle comme:
for (;;) {
// code
break;
}
Macro:
#define BEGIN \
for (;;) {
#define END \
break; }
je préfère utiliser
for(int i = 0; i < 1; ++i) //do once
{
}
c'est l'équivalent de
do
{
}while(0);
et ne donne aucun avertissement.