hacer {…} mientras (falso)

Estaba mirando un código de un individuo y me di cuenta de que parece tener un patrón en sus funciones:

 function() {  do {  } while(false);  } 

No es malo , más peculiar (el código real es bastante limpio y nada sorprendente). No es algo que haya visto antes y me pregunto si alguien puede pensar en alguna lógica detrás de esto, ¿antecedentes en un idioma diferente quizás?

Puedes salir de do{...}while(false) .

Mucha gente señala que a menudo se utiliza con el descanso como una forma incómoda de escribir “goto”. Eso es probablemente cierto si está escrito directamente en la función.

En una macro, OTOH, haz {algo; } while (false) es una forma conveniente de FORCE un punto y coma después de la invocación de la macro, no se permite ningún otro token en absoluto.

Y otra posibilidad es que alguna vez hubo un bucle allí o se anticipa que la iteración se agregará en el futuro (por ejemplo, en el desarrollo basado en pruebas, no fue necesaria la iteración para pasar las pruebas, pero lógicamente tendría sentido hacer un bucle allí si la función debe ser algo más general de lo que se requiere actualmente)

La ruptura como goto es probablemente la respuesta, pero propondré otra idea.

Tal vez quería tener variables definidas localmente y utilizó esta construcción para obtener un nuevo scope.

Recuerde que mientras C ++ reciente permite {...} cualquier lugar, este no siempre fue el caso.

Lo he visto como un patrón útil cuando hay muchos posibles puntos de salida para la función, pero siempre se requiere el mismo código de limpieza, independientemente de cómo salga la función.

Puede hacer que un arduo árbol if / else-if sea mucho más fácil de leer, simplemente teniendo que romper cada vez que se alcanza un punto de salida, con el rest de la lógica en línea después.

Este patrón también es útil en idiomas que no tienen una statement goto. Quizás es allí donde el progtwigdor original aprendió el patrón.

He visto un código así para que puedas usar break como un goto de géneros.

Esta es solo una perversión de while que obtener las sematics de goto tidy-up sin usar la palabra goto.

Es mala forma porque cuando usas otros bucles dentro del exterior while los breaks vuelven ambiguos para el lector. “¿Se supone que esto va a salir? ¿O es solo para salir del circuito interno?”

Creo que es más conveniente escribir break lugar de goto end . Ni siquiera tiene que idear un nombre para la etiqueta que aclare la intención: no quiere saltar a una etiqueta con un nombre específico. Quieres salir de aquí.

También es probable que necesite las llaves de todos modos. Así que este es el do{...}while(false); versión:

 do { // code if (condition) break; // or continue // more code } while(false); 

Y esta es la forma en que tendrías que expresslo si quisieras usar goto :

 { // code if (condition) goto end; // more code } end: 

Creo que el significado de la primera versión es mucho más fácil de entender. También es más fácil de escribir, más fácil de extender, más fácil de traducir a un idioma que no admite goto , etc.


La preocupación más frecuentemente mencionada sobre el uso de la break es que es un goto mal disfrazado. Pero en realidad el break tiene más similitud con el return : ambas instrucciones saltan de un bloque de código que está estructurado en comparación con goto . Sin embargo, ambas instrucciones permiten múltiples puntos de salida en un bloque de código que a veces puede ser confuso. Después de todo, trataría de buscar la solución más clara, sea lo que sea que esté en la situación específica.

Este truco lo usan los progtwigdores que son demasiado tímidos para usar un goto explícito en su código. El autor del código anterior quería tener la capacidad de saltar directamente al punto de “limpieza y devolución” desde la mitad del código. Pero no querían usar una etiqueta y un goto explícito. En cambio, pueden usar un break dentro del cuerpo del ciclo “falso” anterior para lograr el mismo efecto.

Es una práctica muy común. En C Trato de pensar en ello como si quisieras mentirte a ti mismo de una manera “No estoy usando un goto “. Pensando en eso, no habría nada de malo con un goto usado de manera similar. De hecho, también reduciría el nivel de sangría.

Dicho esto, sin embargo, me di cuenta, muy a menudo esto sí … do..while bucles tienden a crecer. Y luego obtienen if s y else s dentro, haciendo que el código en realidad no sea muy legible, y mucho menos comprobable.

Esos … do..while normalmente están destinados a hacer una limpieza . Por todos los medios, preferiría usar RAII y regresar temprano de una función corta . Por otro lado, C no le ofrece tantas comodidades como C ++ , por lo que do..while uno de los mejores enfoques para hacer una limpieza.

Parece un progtwigdor en C En C ++, las variables automáticas tienen destructores que se usan para limpiar, por lo que no debería haber nada necesario para poner en orden antes de la devolución. En C, no tenía este modismo RAII , por lo que si tiene un código de limpieza común, puede goto él o utilizar un ciclo de acceso directo como se indicó anteriormente.

Su principal desventaja en comparación con el modismo de C ++ es que no se pondrá en orden si se lanza una excepción en el cuerpo. C no tenía excepciones, así que esto no era un problema, pero sí lo convierte en un mal hábito en C ++.

Tal vez se usa para que el break pueda usarse dentro para abortar la ejecución de más código en cualquier punto:

 do { if (!condition1) break; some_code; if (!condition2) break; some_further_code; // … } while(false); 

Creo que esto se hace para usar declaraciones break o continue . Algún tipo de lógica de código “goto”.

Es simple: al parecer, puedes saltarte del bucle falso en cualquier momento usando la statement de break . Además, el bloque do es un ámbito separado (que también se puede lograr con { ... } solamente).

En tal situación, podría ser una mejor idea usar RAII (objetos que se destruyen automáticamente cuando finaliza la función). Otra construcción similar es el uso de goto – sí, sé que es malo , pero se puede usar para tener un código de limpieza común como ese:

  function() {  
error: }

(Como comentario aparte: el constructo do-while-false se usa en Lua para obtener la statement de continue faltante).

Muchos respondedores dieron la razón para do{(...)break;}while(false) . Me gustaría complementar la imagen con otro ejemplo de la vida real.

En el siguiente código, tuve que establecer la operation enumerador operation función de la dirección apuntada por data puntero de data . Debido a que una caja de interruptores se puede usar solo en los tipos escalares primero lo hice de manera ineficiente de esta manera

 if (data == &array[o1]) operation = O1; else if (data == &array[o2]) operation = O2; else if (data == &array[on]) operation = ON; Log("operation:",operation); 

Pero como Log () y el rest del código se repite para cualquier valor de operación elegido, estaba vagando por cómo omitir el rest de las comparaciones cuando la dirección ya se ha descubierto. Y aquí es donde do{(...)break;}while(false) es útil.

 do { if (data == &array[o1]) { operation = O1; break; } if (data == &array[o2]) { operation = O2; break; } if (data == &array[on]) { operation = ON; break; } } while (false); Log("operation:",operation); 

Uno puede preguntarse por qué no pudo hacer lo mismo con la break en una statement if , como:

 if (data == &array[o1]) { operation = O1; break; } else if (...) 

break interactúa únicamente con el bucle o conmutador circundante más cercano, ya sea un for , while o do .. while type, por lo que desafortunadamente eso no funcionará.

Varias explicaciones. El primero es general, el segundo es específico para macros de preprocesador C con parámetros:

Control de flujo

He visto esto usado en el código C simple. Básicamente, es una versión más segura de goto, ya que puedes salir de ella y toda la memoria se limpia correctamente.

¿Por qué algo como goto sería bueno? Bueno, si tiene un código donde casi todas las líneas pueden devolver un error, pero debe reactjsr a todas de la misma manera (por ejemplo, entregándole el error a su interlocutor después de la limpieza), generalmente es más legible evitar una if( error ) { /* cleanup and error string generation and return here */ } ya que evita la duplicación del código de limpieza.

Sin embargo, en C ++ tienes excepciones + RAII exactamente para este propósito, por lo que consideraría que es un mal estilo de encoding.

Control de semiconductor

Si olvida el punto y coma después de una macro invocación de función, los argumentos pueden contraerse de forma no deseada y comstackrse en una syntax válida. Imagina la macro

 #define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo"); 

Eso es llamado accidentalmente como

 if( foo ) PRINT_IF_DEBUGMODE_ON("Hullo\n") else doSomethingElse(); 

El “else” se considerará asociado con gDebugModeOn , por lo que cuando foo es false , ocurrirá el reverso exacto de lo que se pretendía.

Proporcionando un scope para variables temporales.

Como do / while tiene llaves, las variables temporales tienen un scope claramente definido del que no pueden escapar.

Evitar advertencias de “posiblemente punto y coma no deseado”

Algunas macros solo se activan en comstackciones de depuración. Usted los define como:

 #if DEBUG #define DBG_PRINT_NUM(n) printf("%d\n",n); #else #define DBG_PRINT_NUM(n) #endif 

Ahora, si usa esto en una comstackción de lanzamiento dentro de un condicional, se comstack para

 if( foo ) ; 

Muchos comstackdores ven esto como lo mismo que

 if( foo ); 

Que a menudo se escribe accidentalmente. Entonces recibes una advertencia. The do {} while (false) lo oculta del comstackdor y lo acepta como una indicación de que realmente no desea hacer nada aquí.

Evitar la captura de líneas por condicionales

Macro del ejemplo anterior:

 if( foo ) DBG_PRINT_NUM(42) doSomething(); 

Ahora, en una comstackción de depuración, dado que habitualmente también incluimos el punto y coma, esto comstack muy bien. Sin embargo, en la comstackción de lanzamiento esto de repente se convierte en:

 if( foo ) doSomething(); 

O más claramente formateado

 if( foo ) doSomething(); 

Que no es en absoluto lo que se pretendía. Agregar un do {…} while (false) alrededor de la macro convierte el punto y coma faltante en un error de comstackción.

¿Qué significa eso para el OP?

En general, desea utilizar excepciones en C ++ para el manejo de errores y plantillas en lugar de macros. Sin embargo, en el muy raro caso de que aún necesite macros (por ejemplo, al generar nombres de clases mediante el uso de token de fichas) o esté restringido a C simple, este es un patrón útil.

¿Qué edad tenía el autor?

Lo pregunto porque una vez me encontré con un código Fortran en tiempo real que lo hizo, a fines de los 80’s. Resulta que es una forma realmente buena de simular hilos en un sistema operativo que no los tiene. Simplemente coloque todo el progtwig (su planificador) en un bucle y llame a sus rutinas “hilo” “una por una. Las rutinas de hilo en sí son bucles que se repiten hasta que ocurre una de una serie de condiciones (a menudo una es una cierta cantidad de ha pasado el tiempo). Es una “multitarea cooperativa”, ya que depende de los hilos individuales renunciar a la CPU de vez en cuando para que los demás no se mueran de hambre. Puede anidar las llamadas al subprogtwig en bucle para simular prioridad de subprocesos alzacuello.

Además de los ya mencionados ‘ejemplos de goto’, la expresión do … while (0) a veces se usa en una definición de macro para proporcionar paréntesis en la definición y aún así el comstackdor trabaja con la adición de un punto y coma al final de una macro llamada.

http://groups.google.com/group/comp.soft-sys.ace/browse_thread/thread/52f670f1292f30a4?tvc=2&q=while+(0)

Estoy de acuerdo con la mayoría de los carteles sobre el uso como un truco disfrazado. Las macros también se han mencionado como una posible motivación para escribir código en el estilo.

También he visto esta construcción utilizada en entornos mixtos de C / C ++ como la excepción de un hombre pobre. El “do {} while (false)” con un “break” se puede usar para saltar al final del bloque de código si algo que normalmente garantizara una excepción se encuentra en el ciclo.

También he indicado que esta construcción se usa en tiendas donde se aplica la ideología de “retorno único por función”. De nuevo, esto es en lugar de un “goto” explícito, pero la motivación es evitar múltiples puntos de devolución, no “saltear” el código y continuar la ejecución real dentro de esa función.

Trabajo con Adobe InDesign SDK, y los ejemplos de InDesign SDK tienen casi todas las funciones escritas así. Es debido al hecho de que la función suele ser muy larga. Donde necesita hacer QueryInterface (…) para obtener algo del modelo de objetos de la aplicación. Por lo general, cada QueryInterface es seguida por if no fue así, se rompe.

Muchos ya han declarado la similitud entre este constructo y un goto , y expresson una preferencia por el goto. ¿Quizás los antecedentes de esta persona incluían un entorno en el que los goto estaban estrictamente prohibidos por las pautas de encoding?

La otra razón por la que se me ocurre es que decora los frenos, mientras que creo que en un nuevo estándar de C ++ los frenillos desnudos no están bien (a ISO C no le gustan). De lo contrario, silenciar un analizador estático como pelusa.

No estoy seguro de por qué los querría, tal vez un scope variable o una ventaja con un depurador.

Consulte Trivial Do While loop , y Braces son buenos desde C2.

Para aclarar mi terminología (que creo sigue el uso estándar):

Llaves desnudas :

 init(); ... { c = NULL; mkwidget(&c); finishwidget(&c); } shutdown(); 

Llaves vacías (NOP):

 {} 

p.ej

 while (1) {} /* Do nothing, endless loop */ 

Bloque :

 if (finished) { closewindows(&windows); freememory(&cache); } 

que se convertiría

 if (finished) closewindows(&windows); freememory(&cache); 

si se eliminan las llaves, lo que altera el flujo de ejecución, no solo el scope de las variables locales. Por lo tanto, no ‘independiente’ o ‘desnudo’.

Las llaves desnudas o un bloque se pueden usar para indicar cualquier sección de código que pueda ser un potencial para una función (en línea) que desea marcar, pero no refactorizar en ese momento.

Es una forma artificial de emular un GOTO ya que estos dos son prácticamente idénticos:

 // NOTE: This is discouraged! do { if (someCondition) break; // some code be here } while (false); // more code be here 

y:

 // NOTE: This is discouraged, too! if (someCondition) goto marker; // some code be here marker: // more code be here 

Por otro lado, ambos deberían hacerse realmente con s:

 if (!someCondition) { // some code be here } // more code be here 

Aunque el anidamiento puede ser un poco feo si solo conviertes una cadena larga de GOTO hacia adelante en nesteds if s. La verdadera respuesta es la refactorización propiamente dicha, sin imitar los constructos del lenguaje arcaico.

Si estabas tratando desesperadamente de transliterar un algoritmo con GOTO s en él, probablemente podrías hacerlo con este modismo. Sin embargo, ciertamente no es estándar y es un buen indicador de que no se está apegando estrechamente a las expresiones idiomáticas esperadas.

No conozco ningún lenguaje tipo C en el que do / while sea una solución idiomática para nada, en realidad.

Probablemente puedas refactorizar todo el desorden en algo más sensato para hacerlo más idiomático y mucho más legible.

Algunos codificadores prefieren tener solo una única salida / devolución de sus funciones. El uso de un dummy do {….} while (falso); le permite “salir” del bucle ficticio una vez que haya terminado y aún tener un retorno único.

Soy un codificador de Java, así que mi ejemplo sería algo así como

 import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; public class p45 { static List cakeNames = Arrays.asList("schwarzwald torte", "princess", "icecream"); static Set forbidden = Stream.of(0, 2).collect(Collectors.toSet()); public static void main(String[] argv) { for (int i = 0; i < 4; i++) { System.out.println(String.format("cake(%d)=\"%s\"", i, describeCake(i))); } } static String describeCake(int typeOfCake) { String result = "unknown"; do { // ensure type of cake is valid if (typeOfCake < 0 || typeOfCake >= cakeNames.size()) break; if (forbidden.contains(typeOfCake)) { result = "not for you!!"; break; } result = cakeNames.get(typeOfCake); } while (false); return result; } } 

Esto es divertido. Probablemente hay rupturas dentro del ciclo, como otros han dicho. Lo habría hecho de esta manera:

 while(true) { 
break; // at the end. }