Je regardais du code par un individu et j'ai remarqué qu'il semble avoir un modèle dans ses fonctions:
<return-type> function(<params>)
{
<initialization>
do
{
<main code for function>
}
while(false);
<tidy-up & return>
}
Ce n'est pas mal , c'est plus particulier (le code actuel est assez soigné et sans surprise). Ce n'est pas quelque chose que j'ai vu auparavant et je me suis demandé si quelqu'un pouvait penser à une logique derrière cela - un fond dans une langue différente peut-être?
do...while()
et il a utilisé ce code comme modèle de fonction depuis.Réponses:
Vous pouvez
break
sortir dedo{...}while(false)
.la source
Beaucoup de gens soulignent qu'il est souvent utilisé avec break comme une manière maladroite d'écrire "goto". C'est probablement vrai s'il est écrit directement dans la fonction.
Dans une macro, OTOH,
do { something; } while (false)
est un moyen pratique de FORCER un point-virgule après l'appel de la macro, absolument aucun autre jeton n'est autorisé à suivre.Et une autre possibilité est qu'il y ait eu une boucle là-bas ou qu'une itération devrait être ajoutée dans le futur (par exemple dans le développement piloté par les tests, l'itération n'était pas nécessaire pour passer les tests, mais logiquement, il serait logique de faire une boucle là-bas si la fonction devait être un peu plus générale que ce qui est actuellement requis)
la source
La pause comme goto est probablement la réponse, mais je vais avancer une autre idée.
Peut-être qu'il voulait avoir une variable définie localement et a utilisé cette construction pour obtenir une nouvelle portée.
Rappelez-vous que si le C ++ récent permet
{...}
n'importe où, ce n'était pas toujours le cas.la source
{}
n'importe où . C'est particulièrement pratique dans leswitch-case
code{...}
n'importe où est un développement plus moderne en C ++ (rappelez-vous que le premier C ++ que j'ai utilisé a été implémenté avec un pré-processeur et qui ne permettait pas cette utilisation moderne.) l'auteur était juste une vieille école.Je l'ai vu utilisé comme un modèle utile lorsqu'il existe de nombreux points de sortie potentiels pour la fonction, mais le même code de nettoyage est toujours requis quelle que soit la façon dont la fonction se termine.
Cela peut rendre un arbre if / else-if fastidieux beaucoup plus facile à lire, en ayant simplement à casser chaque fois qu'un point de sortie est atteint, avec le reste de la logique en ligne par la suite.
Ce modèle est également utile dans les langages qui n'ont pas d'instruction goto. C'est peut-être là que le programmeur d'origine a appris le modèle.
la source
J'ai vu du code comme celui-là pour que vous puissiez l'utiliser
break
en quelquegoto
sorte.la source
Ceci est juste une perversion de
while
pour obtenir la sématique degoto tidy-up
sans utiliser le mot goto.Il est mauvaise forme parce que lorsque vous utilisez d' autres boucles dans la partie extérieure
while
dubreaks
devenir ambigu au lecteur. "Est-ce que c'est censé sortir? Ou est-ce uniquement destiné à sortir de la boucle intérieure?"la source
Je pense qu'il est plus pratique d'écrire
break
au lieu degoto end
. Vous n'avez même pas besoin de trouver un nom pour l'étiquette qui rend l'intention plus claire: vous ne voulez pas sauter à une étiquette avec un nom spécifique. Tu veux sortir d'ici.Il y a également de fortes chances que vous ayez besoin des accolades de toute façon. Voici donc la
do{...}while(false);
version:Et c'est ainsi qu'il vous faudrait l'exprimer si vous vouliez utiliser
goto
:Je pense que la signification de la première version est beaucoup plus facile à saisir. De plus, il est plus facile d'écrire, plus facile à étendre, plus facile à traduire dans une langue qui ne prend pas en charge
goto
, etc.La préoccupation la plus fréquemment mentionnée à propos de l'utilisation de
break
est qu'il est mal déguiségoto
. Mais a enbreak
fait plus de ressemblance avecreturn
: Les deux instructions sortent d'un bloc de code qui est à peu près structuré par rapport àgoto
. Néanmoins, les deux instructions autorisent plusieurs points de sortie dans un bloc de code, ce qui peut parfois prêter à confusion. Après tout, j'essaierais d'opter pour la solution la plus claire, quelle que soit la situation spécifique.la source
do{ ... }while(false);
en général. Mais en fait, il s'agit de l'utiliser pour émuler une sorte detry{ ... }finally{ ... }
.Cette astuce est utilisée par les programmeurs qui sont trop timides pour utiliser un explicite
goto
dans leur code. L'auteur du code ci-dessus voulait avoir la possibilité de sauter directement au point "nettoyage et retour" à partir du milieu du code. Mais ils ne voulaient pas utiliser d'étiquette et explicitegoto
. Au lieu de cela, ils peuvent utiliser unbreak
à l'intérieur du corps du cycle «faux» ci-dessus pour obtenir le même effet.la source
C'est une pratique très courante. En C . J'essaie d'y penser comme si vous vouliez vous mentir d' une manière "je n'utilise pas un
goto
". En y réfléchissant, il n'y aurait rien de mal à unegoto
utilisation similaire. En fait, cela réduirait également le niveau d'indentation.Cela dit, cependant, j'ai remarqué que très souvent ces
do..while
boucles ont tendance à se développer. Et puis ils obtiennent desif
s et deselse
s à l'intérieur, ce qui rend le code en fait pas très lisible, encore moins testable.Ceux-ci
do..while
sont normalement destinés à faire un nettoyage . Par tous les moyens possibles, je préférerais utiliser RAII et revenir tôt d'une fonction courte . D'un autre côté, C ne vous offre pas autant de commodités que C ++ , ce qui en fait l'unedo..while
des meilleures approches pour effectuer un nettoyage.la source
Cela ressemble à un programmeur C. En C ++, les variables automatiques ont des destructeurs que vous utilisez pour nettoyer, il ne devrait donc y avoir rien à nettoyer avant le retour. En C, vous n'aviez pas cet idiome RAII , donc si vous avez un code de nettoyage commun, vous l'
goto
utilisez ou utilisez une boucle à passage unique comme ci-dessus.Son principal inconvénient par rapport à l'idiome C ++ est qu'il ne sera pas rangé si une exception est levée dans le corps. C n'avait pas d'exceptions, donc ce n'était pas un problème, mais cela en fait une mauvaise habitude en C ++.
la source
Plusieurs explications. Le premier est général, le second est spécifique aux macros de préprocesseur C avec des paramètres:
Contrôle de flux
J'ai vu cela utilisé dans du code C brut. Fondamentalement, c'est une version plus sûre de goto, car vous pouvez en sortir et toute la mémoire est nettoyée correctement.
Pourquoi quelque chose
goto
comme ça serait bien? Eh bien, si vous avez du code où à peu près chaque ligne peut retourner une erreur, mais vous devez réagir à tous de la même façon (par exemple , en remettant l'erreur à votre correspondant après le nettoyage), il est généralement plus facile à lire pour éviter unif( error ) { /* cleanup and error string generation and return here */ }
comme cela évite la duplication du code de nettoyage.Cependant, en C ++, vous avez des exceptions + RAII exactement dans ce but, donc je considérerais que c'est un mauvais style de codage.
Vérification des points-virgules
Si vous oubliez le point-virgule après un appel de macro de type fonction, les arguments peuvent se contracter d'une manière indésirable et se compiler dans une syntaxe valide. Imaginez la macro
C'est accidentellement appelé comme
Le "else" sera considéré comme étant associé au
gDebugModeOn
, alors quandfoo
c'est le casfalse
, l'inverse exact de ce qui était prévu se produira.Fournir une portée pour les variables temporaires.
Puisque le do / while a des accolades, les variables temporaires ont une portée clairement définie à laquelle elles ne peuvent pas échapper.
Éviter les avertissements de type "point-virgule éventuellement indésirable"
Certaines macros ne sont activées que dans les versions de débogage. Vous les définissez comme:
Maintenant, si vous utilisez ceci dans une version de version à l'intérieur d'un conditionnel, il se compile en
De nombreux compilateurs voient cela comme
Ce qui est souvent écrit accidentellement. Vous recevez donc un avertissement. Le do {} while (false) cache cela au compilateur et est accepté par lui comme une indication que vous ne voulez vraiment rien faire ici.
Éviter la capture de lignes par des conditions
Macro de l'exemple précédent:
Maintenant, dans une version de débogage, puisque nous incluons aussi habituellement le point-virgule, cela compile très bien. Cependant, dans la version de version, cela se transforme soudainement en:
Ou plus clairement formaté
Ce qui n'est pas du tout ce qui était prévu. L'ajout d'un do {...} while (false) autour de la macro transforme le point-virgule manquant en une erreur de compilation.
Qu'est-ce que cela signifie pour l'OP?
En général, vous souhaitez utiliser des exceptions en C ++ pour la gestion des erreurs et des modèles au lieu de macros. Cependant, dans le cas très rare où vous avez encore besoin de macros (par exemple, lorsque vous générez des noms de classe en utilisant le collage de jetons) ou que vous êtes limité au C pur, c'est un modèle utile.
la source
x =1; y = 2; { int tmp = y; y = x; x = tmp; }
.#define DBG_PRINT_NUM(n) {}
.if(a) DBG_PRINT_NUM(n); else other();
il ne se compilera pas. Seulif(a) {} else other();
ouif(a) ; else other();
est valide, maisif(a) {}; else other();
ne l'est pas, car cela fait que la clause "if" se compose de deux instructions.Peut-être qu'il est utilisé pour
break
pouvoir être utilisé à l'intérieur pour abandonner l'exécution de code supplémentaire à tout moment:la source
goto
simplement parce que quelqu'un a entendu dire que c'était «mauvais».Je pense que cela est fait pour utiliser
break
ou descontinue
déclarations. Une sorte de logique de code "goto".la source
C'est simple: apparemment, vous pouvez sortir de la fausse boucle à tout moment en utilisant l'
break
instruction. En outre, ledo
bloc est une portée distincte (qui pourrait également être réalisée{ ... }
uniquement avec ).Dans une telle situation, il peut être préférable d'utiliser RAII (les objets se détruisant automatiquement correctement lorsque la fonction se termine). Une autre construction similaire est l'utilisation de
goto
- oui, je sais que c'est mal , mais il peut être utilisé pour avoir un code de nettoyage commun comme ceci:(En aparté: la construction do-while-false est utilisée dans Lua pour trouver l'
continue
instruction manquante .)la source
De nombreux répondants en ont donné la raison
do{(...)break;}while(false)
. Je voudrais compléter l'image par un autre exemple concret.Dans le code suivant, j'ai dû définir l'énumérateur en
operation
fonction de l'adresse pointée par ledata
pointeur. Parce qu'un boîtier de commutation ne peut être utilisé que sur des types scalaires, je l'ai fait de cette façon de manière inefficaceMais puisque Log () et le reste du code se répètent pour toute valeur d'opération choisie, je me demandais comment ignorer le reste des comparaisons lorsque l'adresse a déjà été découverte. Et c'est là que cela est
do{(...)break;}while(false)
utile.On peut se demander pourquoi il ne pouvait pas faire la même chose avec
break
uneif
déclaration, comme:break
interagit uniquement avec le plus proche englobante boucle ou d'un commutateur, que ce soit unfor
,while
ou ledo .. while
type, donc , malheureusement , cela ne fonctionnera pas.la source
Quel âge avait l'auteur?
Je demande parce que je suis tombé une fois sur un code Fortran en temps réel qui faisait cela, à la fin des années 80. Il s'avère que c'est un très bon moyen de simuler des threads sur un système d'exploitation qui ne les a pas. Il vous suffit de mettre tout le programme (votre planificateur) dans une boucle et d'appeler vos "routines de thread" une par une. Les routines de thread elles-mêmes sont des boucles qui itèrent jusqu'à ce que l'une des conditions se produise (souvent l'une étant une certaine quantité de le temps est passé). Il s'agit d'un "multitâche coopératif", en ce sens qu'il appartient aux threads individuels d'abandonner le processeur de temps en temps pour que les autres ne soient pas affamés. Vous pouvez imbriquer les appels de sous-programme en boucle pour simuler la priorité des threads bandes.
la source
En plus des exemples `` goto '' déjà mentionnés, l'idiome do ... while (0) est parfois utilisé dans une définition de macro pour fournir des crochets dans la définition et que le compilateur travaille toujours en ajoutant un point-virgule à la fin de un appel de macro.
http://groups.google.com/group/comp.soft-sys.ace/browse_thread/thread/52f670f1292f30a4?tvc=2&q= while+(0)
la source
Je suis d'accord avec la plupart des affiches sur l'utilisation comme un goto à peine déguisé. Les macros ont également été mentionnées comme une motivation potentielle pour écrire du code dans le style.
J'ai également vu cette construction utilisée dans les environnements mixtes C / C ++ comme une exception pour les pauvres. Le "do {} while (false)" avec un "break" peut être utilisé pour passer à la fin du bloc de code si quelque chose qui justifierait normalement une exception se produisait dans la boucle.
J'ai également ressenti cette construction utilisée dans les magasins où l'idéologie du «rendement unique par fonction» est appliquée. Encore une fois, cela remplace un "goto" explicite - mais la motivation est d'éviter de multiples points de retour, pas de "sauter" du code et de continuer l'exécution réelle dans cette fonction.
la source
Je travaille avec Adobe InDesign SDK, et les exemples du SDK InDesign ont presque toutes les fonctions écrites comme ça. Cela est dû au fait que les fonctions sont généralement très longues. Où vous devez faire QueryInterface (...) pour obtenir quoi que ce soit du modèle objet de l'application. Donc, généralement, chaque QueryInterface est suivie de
if
pas bien, pause.la source
Beaucoup ont déjà indiqué la similitude entre cette construction et a
goto
, et ont exprimé une préférence pour le goto. Peut-être que les antécédents de cette personne comprenaient un environnement où les goto étaient strictement interdits par les directives de codage?la source
L'autre raison à laquelle je peux penser est qu'il décore les accolades, alors que je crois que dans un nouveau standard C ++, les accolades nues ne sont pas correctes (ISO C ne les aime pas). Sinon pour calmer un analyseur statique comme des peluches.
Vous ne savez pas pourquoi vous les voulez, peut-être une portée variable ou un avantage avec un débogueur.
Voir la boucle Trivial Do While et les accolades sont bonnes à partir de C2.
Pour clarifier ma terminologie (qui, je crois, suit l'utilisation standard):
Bretelles nues :
Accolades vides (NOP):
par exemple
Bloquer :
qui deviendrait
si les accolades sont supprimées, modifiant ainsi le flux d'exécution, pas seulement la portée des variables locales. Donc pas «autonome» ou «nu».
Des accolades nues ou un bloc peuvent être utilisés pour signifier toute section de code qui pourrait être un potentiel pour une fonction (en ligne) que vous souhaitez marquer, mais pas refactoriser à ce moment-là.
la source
C'est un moyen artificiel d'émuler un
GOTO
car ces deux sont pratiquement identiques:et:
D'un autre côté, les deux devraient vraiment être faits avec
if
s:Bien que l'imbrication puisse devenir un peu moche si vous transformez simplement une longue chaîne de forward-
GOTO
s enif
s imbriqués . La vraie réponse est une refactorisation appropriée, cependant, sans imiter les constructions de langage archaïques.Si vous essayiez désespérément de translittérer un algorithme contenant
GOTO
s, vous pourriez probablement le faire avec cet idiome. Ce n'est certainement pas standard et c'est un bon indicateur du fait que vous n'adhérez pas étroitement aux idiomes attendus de la langue.Je ne connais aucun langage de type C où do / while est une solution idiomatique pour quoi que ce soit, en fait.
Vous pourriez probablement refactoriser tout le désordre en quelque chose de plus sensé pour le rendre plus idiomatique et beaucoup plus lisible.
la source
do..while(false)
n'est pas pour exécuter un nombre de fois "indéfini", c'est pour exécuter une fois .continue
instruction conditionnelle à la fin, comme je l'ai montré. Parundefined
je veux simplement dire que vous ne savez pas s'il s'exécute plus d'une fois, sauf si vous pouvez prédire si les conditions seront remplies à une itération spécifique. Sans aucuncontinue
s,do..while(false)
fonctionnera une fois, mais aussi sans aucunbreak
s,while(true)
fonctionnera pour toujours, donc le «comportement par défaut» n'est pas vraiment pertinent pour comprendre ce qui peut être fait avec les boucles.continue
.continue
ne répète PAS la boucle. "L'continue
instruction doit se produire uniquement dans une instruction d'itération et fait passer le contrôle à la partie de continuation de boucle de la plus petite instruction d' itération englobante , c'est-à-dire à la fin de la boucle."Certains codeurs préfèrent n'avoir qu'une seule sortie / retour de leurs fonctions. L'utilisation d'un mannequin do {....} while (false); vous permet de "sortir" de la boucle factice une fois que vous avez terminé et que vous avez toujours un seul retour.
Je suis un codeur Java, donc mon exemple serait quelque chose comme
la source
Dans de tels cas, j'utilise
la source
C'est amusant. Il y a probablement des ruptures à l'intérieur de la boucle comme d'autres l'ont dit. Je l'aurais fait de cette façon:
la source
do..while(false)
sort toujours,while(true)
est plus risqué.while(true)
est l'idiome correct dans la plupart des langues. Vous le trouverez souvent dans les applications GUI en tant que boucle principale du programme. Parce que vous supposez fondamentalement que le programme ne devrait pas mourir tant qu'on ne lui a pas dit de le faire,do..while(false)
cela provoquerait toutes sortes de logiques artificielles. Cette approche peut être plus risquée à partir d'un point de vue perfectionniste, mais elle est sémantiquement plus facile et donc moins sujette aux erreurs pour les programmeurs humains (désolé, Skynet).do{...}while(false)
est exactement le même quewhile(true){ .. break;}
continue
, ils ne sont pas les mêmes.