45 votes

Quelle est l'utilisation possible de "#define for if (false) {} else for" ?

Dans une autre question, je viens de repérer cette petite perle de C sagesse :

#define for if (false) {} else for

ce qui a fait que MSVC a craché des avertissements "expression constante" pour une déclaration tout à fait valide :

for (int i = 0; i <= 10; i++) {...}

Je comprends pourquoi MSVC se plaint parce qu'il s'étend pour :

if (false) {} else for (int i = 0; i <= 10; i++) {...}

Je ne comprends pas pourquoi les développeurs ont utilisé ce petit bout de papier. Quelqu'un a une idée ?

92voto

Adam Rosenfield Points 176408

Il s'agit de corriger un bogue dans les anciennes versions de Visual C++ (v6.0 et antérieures). Dans le passé, Visual C++ ne respectait pas les règles de portée concernant les variables déclarées à l'intérieur des fichiers for déclarations :

// This compiles in old versions of Visual C++, but it is in fact INVALID C++
for(int i = 0; ...)
{
    ...
}

for(i = 0; ...)
{

}

En d'autres termes, Visual C++ donne i une portée comme s'il était déclaré en dehors de la boucle, et il vous permet de continuer à l'utiliser après la fin de la boucle. Cela conduit à du code tel que le snippet ci-dessus. Dans les compilateurs plus conformes aux normes, i n'est plus en vigueur lors de la définition de la seconde for le compilateur émet donc une erreur à propos de i étant indéfini.

Pour résoudre ce problème, certaines personnes ont utilisé cette macro (ou des macros équivalentes très similaires) :

#define for if(0) {} else for

Cela modifie le for dans cette boucle :

if(0)
{
}
else
    for(int i = 0; ...)
    {
        ...
    }

Cela met le for à un niveau supplémentaire de portée, de sorte que toutes les variables déclarées dans la boucle for sera hors de portée par la suite, quel que soit le bug de Visual C++. Cela permet de s'assurer que le même code compile correctement de manière cohérente à la fois dans Visual C++ et dans les compilateurs conformes aux normes, et que le code incorrect ne compile pas correctement de manière cohérente.

Notez également que si la macro était plutôt définie comme suit :

// DO NOT USE
#define for if(1) for

Alors que cela aurait le même effet pour un code simple, cela provoquerait soudainement une compilation incorrecte du code suivant :

if(foo)
    for(...)
    {
        ...
    }
else
    doSomething();

Parce que si vous développez la macro, vous obtenez ceci :

if(foo)
    if(1)
        for(...)
        {
            ...
        }
    else
        doSomething();

Et le else correspond maintenant à la mauvaise if ! Ainsi, l'utilisation astucieuse de if(0) {} else au lieu de if(1) permet d'éviter ce problème.

En guise de conclusion, #define for if(0) {} else for ne provoque pas de récursion infinie, car le préprocesseur ne remplacera pas récursivement la macro que vous êtes en train de définir. Il n'effectuera qu'un seul remplacement dans ce cas.

7voto

Earlz Points 19355

D'après une recherche rapide, c'est un bogue dans MSVC qui est surmonté.

Comme je le comprends,

for(int i=0...){.....} 
//later at the same scope level in the same function
for(int i=0...){...}

provoquera une erreur de redéfinition de 'i'.

Si l'instruction for est incluse dans une instruction if, le compilateur fonctionne comme il le devrait et il n'y a pas d'erreur de redéfinition (apparemment, il interprète les niveaux de portée de 'if' mais pas de 'for').

2voto

Parce que le compilateur msvc gère incorrectement la portée des variables déclarées dans l'instruction for par défaut. Pour éviter ce comportement, il fallait désactiver les extensions microsoft, ce qui empêchait la compilation des en-têtes ms.

J'utilise (oui, j'utilise toujours vs6) un autre qui ne provoque pas l'avertissement dans vs6, bien que le compilateur Intel le repère toujours.

#define for switch(0) case 0: default: for

Je ne me souviens plus où je l'ai trouvé, mais je doute l'avoir inventé ;-)

Je sais que les autres réponses disent déjà la plupart de ces choses, mais la fenêtre contextuelle demande de s'assurer que vous répondez à la question.

1voto

Albert Points 12642

L'effet a déjà été décrit.

La raison de l'avoir est de porter du code C++ vers MSVC. Il est également très utile si vous souhaitez que votre code C++ soit indépendant de la plate-forme. Par exemple, vous l'avez développé sur Linux/MacOSX et vous voulez maintenant le compiler dans MSVC.

Et il est également très utile pour le C++ lui-même. Par exemple :

for(std::set<Foo>::iterator i = myset.begin(); i != myset.end(); ++i) {
    // ...
}

for(int i = 0; i < N; ++i) {
    // ...
}

J'ai vu du code MSVC qui contournait ce problème en faisant l'un ou l'autre :

for(std::set<Foo>::iterator i1 = myset.begin(); i1 != myset.end(); ++i1) {
    // ...
}

for(int i2 = 0; i2 < N; ++i2) {
    // ...
}

Ou :

{for(std::set<Foo>::iterator i = myset.begin(); i != myset.end(); ++i) {
    // ...
}}

{for(int i = 0; i < N; ++i) {
    // ...
}}

Dans les deux cas (imo) pas très agréable. Et ce #define est un petit hack pour que MSVC se comporte de manière plus standard.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X