Estaba mirando un código por un individuo y noté que parece tener un patrón en sus funciones:
<return-type> function(<params>)
{
<initialization>
do
{
<main code for function>
}
while(false);
<tidy-up & return>
}
No está mal , es más peculiar (el código real es bastante ordenado y no sorprende). No es algo que haya visto antes y me preguntaba si alguien puede pensar en alguna lógica detrás de él, ¿quizás en un idioma diferente?
do...while()
y ha usado ese código como plantilla de función desde entonces.Respuestas:
Puedes
break
salir dedo{...}while(false)
.fuente
Mucha gente señala que a menudo se usa con break como una forma incómoda de escribir "goto". Eso probablemente sea cierto si está escrito directamente en la función.
En una macro, OTOH,
do { something; } while (false)
es una forma conveniente de FORZAR un punto y coma después de la invocación de la macro, absolutamente ningún otro token puede seguir.Y otra posibilidad es que haya una vez un bucle allí o se anticipe que se agregará iteración 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 hacerlo allí si la función debía ser algo más general de lo que se requiere actualmente)
fuente
El descanso como goto es probablemente la respuesta, pero propondré otra idea.
Tal vez quería tener variables definidas localmente y usó esta construcción para obtener un nuevo alcance.
Recuerde que si bien C ++ reciente permite
{...}
cualquier lugar, este no siempre fue el caso.fuente
{}
cualquier lugar ... Esto es especialmente útil en elswitch-case
código{...}
cualquier lugar hay un desarrollo más moderno en C ++ (recuerde que el primer C ++ que utilicé se implementó con un preprocesador y eso no permitió este uso moderno). Quizás el autor era simplemente de la vieja escuela.He visto que se usa como un patrón útil cuando hay muchos puntos de salida potenciales 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 árbol cansador if / else-if sea mucho más fácil de leer, simplemente teniendo que romperse cada vez que se alcanza un punto de salida, con el resto de la lógica en línea después.
Este patrón también es útil en idiomas que no tienen una instrucción goto. Quizás ahí es donde el programador original aprendió el patrón.
fuente
He visto un código así para que pueda usarlo
break
como unagoto
especie de.fuente
Esto es solo una perversión de
while
obtener la semántica degoto tidy-up
sin usar la palabra goto.Es de mal gusto ya que al usar otros circuitos en el interior del exterior de
while
labreaks
vuelven ambiguos para el lector. "¿Se supone que esto tiene que salir? ¿O está destinado solo a salir del circuito interno?"fuente
Creo que es más conveniente escribir en
break
lugar degoto end
. Ni siquiera tiene que pensar en un nombre para la etiqueta que aclare la intención: no desea saltar a una etiqueta con un nombre específico. Quieres salir de aquí.También es probable que necesite los frenos de todos modos. Entonces esta es la
do{...}while(false);
versión:Y esta es la forma en que tendrías que expresarlo si quisieras usar
goto
: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 es compatible
goto
, etc.La preocupación más frecuentemente mencionada sobre el uso de
break
es que está muy mal disfrazadagoto
. Pero en realidadbreak
tiene más parecido areturn
: Ambas instrucciones saltan de un bloque de código que está más o menos estructurado en comparación congoto
. 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, cualquiera que sea la situación específica.fuente
do{ ... }while(false);
en general. Pero en realidad se trata de usarlo para emular algún tipo detry{ ... }finally{ ... }
.Este truco lo usan los programadores que son demasiado tímidos para usar un
goto
código explícito . El autor del código anterior quería tener la capacidad de saltar directamente al punto de "limpieza y retorno" desde la mitad del código. Pero no querían usar una etiqueta explícitagoto
. En cambio, pueden usar elbreak
interior del cuerpo del ciclo "falso" anterior para lograr el mismo efecto.fuente
Es una práctica muy común. En do . Trato de pensar en ello como si quisieras mentirte a ti mismo de una manera "No estoy usando un
goto
". Pensando en ello, no habría nada de malo con ungoto
uso similar. De hecho, también reduciría el nivel de sangría.Sin embargo, dicho esto, noté, muy a menudo estos
do..while
bucles tienden a crecer. Y luego obtienenif
syselse
dentro, lo que hace 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 posibles, preferiría usar RAII y regresar temprano de una función corta . Por otro lado, C no le brinda tantas comodidades como lo hace C ++ , por lo que esdo..while
uno de los mejores enfoques para hacer una limpieza.fuente
Parece un programador en C. En C ++, las variables automáticas tienen destructores que se usan para limpiar, por lo que no debe haber nada necesario para ordenar antes del regreso. En C, no tenía este modismo RAII , por lo que si tiene un código de limpieza común, lo
goto
usa o usa un ciclo de paso único como se indicó anteriormente.Su principal desventaja en comparación con el idioma de C ++ es que no se ordenará si se produce una excepción en el cuerpo. C no tenía excepciones, así que esto no fue un problema, pero sí lo convierte en un mal hábito en C ++.
fuente
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 código C simple. Básicamente, es una versión más segura de goto, ya que puede salir de ella y toda la memoria se limpia correctamente.
¿Por qué algo
goto
bueno sería bueno? Bueno, si usted tiene código donde prácticamente cada línea puede devolver un error, pero que necesita para reaccionar a todos ellos de la misma manera (por ejemplo, mediante la presentación del error al que llama después de limpiar), por lo general es más fácil de leer para evitar unaif( error ) { /* cleanup and error string generation and return here */ }
como evita la duplicación del código de limpieza.Sin embargo, en C ++ tiene excepciones + RAII exactamente para este propósito, por lo que lo consideraría un mal estilo de codificación.
Comprobación de punto y coma
Si olvida el punto y coma después de una invocación de macro similar a una función, los argumentos pueden contraerse de forma no deseada y compilarse en una sintaxis válida. Imagina la macro
Eso se llama accidentalmente como
El "más" se considerará asociado con el
gDebugModeOn
, por lo tanto, cuandofoo
seafalse
, sucederá exactamente lo contrario de lo que se pretendía.Proporcionando un alcance para variables temporales.
Dado que do / while tiene llaves, las variables temporales tienen un alcance claramente definido del que no pueden escapar.
Evitar advertencias de "punto y coma posiblemente no deseado"
Algunas macros solo se activan en versiones de depuración. Los define como:
Ahora, si usa esto en una versión de lanzamiento dentro de un condicional, se compila a
Muchos compiladores ven esto como lo mismo que
Que a menudo se escribe accidentalmente. Entonces recibes una advertencia. Do {} while (false) oculta esto del compilador y es aceptado por él como una indicación de que realmente no desea hacer nada aquí.
Evitar la captura de líneas por condicionales
Macro del ejemplo anterior:
Ahora, en una compilación de depuración, ya que habitualmente también incluimos el punto y coma, esto se compila muy bien. Sin embargo, en la versión de lanzamiento, esto de repente se convierte en:
O más claramente formateado
Lo cual no es en absoluto lo que se pretendía. Agregar un do {...} while (falso) alrededor de la macro convierte el punto y coma faltante en un error de compilació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 caso muy raro en el que todavía necesita macros (por ejemplo, cuando genera nombres de clase usando pegado de token) o está restringido a C simple, este es un patrón útil.
fuente
x =1; y = 2; { int tmp = y; y = x; x = tmp; }
.#define DBG_PRINT_NUM(n) {}
.if(a) DBG_PRINT_NUM(n); else other();
, no se compilará. Soloif(a) {} else other();
oif(a) ; else other();
es válido, peroif(a) {}; else other();
no lo es, porque eso hace que la cláusula "if" consista en dos declaraciones.Tal vez se usa para que
break
pueda usarse dentro para abortar la ejecución de código adicional en cualquier momento:fuente
goto
solo porque alguien escuchó que es "malo".Creo que esto se hace para usar
break
ocontinue
declaraciones. Algún tipo de lógica de código "goto".fuente
Es simple: aparentemente puedes salirte del bucle falso en cualquier momento usando la
break
declaración. Además, eldo
bloque es un alcance separado (que también se podría lograr{ ... }
solo).En tal situación, podría ser una mejor idea usar RAII (los objetos 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 este:(Como comentario aparte: la construcción do-while-false se usa en Lua para encontrar la
continue
declaración que falta ).fuente
Muchos respondedores dieron la razón
do{(...)break;}while(false)
. Me gustaría complementar la imagen con otro ejemplo más de la vida real.En el siguiente código, tuve que establecer un enumerador
operation
basado en la dirección apuntada por eldata
puntero. Debido a que una caja de cambio solo se puede usar en tipos escalares primero, lo hice de manera ineficiente de esta maneraPero como Log () y el resto del código se repiten para cualquier valor de operación elegido, estaba divagando cómo omitir el resto de las comparaciones cuando la dirección ya se ha descubierto. Y aquí es donde
do{(...)break;}while(false)
resulta útil.Uno puede preguntarse por qué no podría hacer lo mismo
break
en unif
declaración, como:break
interactúa únicamente con el cerramiento más cercano de bucle o un interruptor, ya sea unafor
,while
odo .. while
tipo, por lo que lamentablemente eso no va a funcionar.fuente
¿Cuántos años tenía el autor?
Pregunto porque una vez me encontré con un código Fortran en tiempo real que hizo eso, a fines de los años 80. Resulta que es una muy buena manera de simular hilos en un sistema operativo que no los tiene. Simplemente coloca todo el programa (su programador) en un bucle y llama a sus rutinas de "subproceso" una por una. Las rutinas de subprocesos 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 "multitarea cooperativa", ya que depende de los hilos individuales abandonar la CPU de vez en cuando para que los demás no se mueran de hambre. Puede anidar las llamadas de subprograma en bucle para simular la prioridad del hilo bandas.
fuente
Además de los 'ejemplos de goto' ya mencionados, el idioma do ... while (0) a veces se usa en una definición de macro para proporcionar corchetes en la definición y aún hacer que el compilador funcione agregando un punto y coma al final de Una llamada macro.
http://groups.google.com/group/comp.soft-sys.ace/browse_thread/thread/52f670f1292f30a4?tvc=2&q=while+(0)
fuente
Estoy de acuerdo con la mayoría de los carteles sobre el uso como un goto apenas disfrazado. Las macros también se han mencionado como una motivación potencial para escribir código en el estilo.
También he visto esta construcción utilizada en entornos mixtos 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 en caso de que se encuentre algo que normalmente garantizaría una excepción en el bucle.
También he enviado esta construcción utilizada en tiendas donde se aplica la ideología de "retorno único por función". Nuevamente, esto es en lugar de un "goto" explícito, pero la motivación es evitar múltiples puntos de retorno, no "omitir" el código y continuar la ejecución real dentro de esa función.
fuente
Trabajo con Adobe InDesign SDK, y los ejemplos de InDesign SDK tienen casi todas las funciones escritas de esta manera. Es debido al hecho de que la función suele ser realmente larga. Donde necesita hacer QueryInterface (...) para obtener cualquier cosa del modelo de objetos de la aplicación. Por lo general, cada QueryInterface es seguido por
if
no salió bien, descanso.fuente
Muchos ya han declarado la similitud entre este constructo y a
goto
, y expresaron una preferencia por el goto. ¿Quizás los antecedentes de esta persona incluían un entorno donde los goto estaban estrictamente prohibidos por las pautas de codificación?fuente
La otra razón por la que se me ocurre es que decora los aparatos ortopédicos, mientras que creo que los aparatos desnudos estándar más nuevos de C ++ no están bien (a ISO C no le gustan). De lo contrario, para silenciar un analizador estático como pelusa.
No estoy seguro de por qué los querría, tal vez alcance variable o ventaja con un depurador.
Ver Trivial Do While loop , y las llaves son buenas de C2.
Para aclarar mi terminología (que creo que sigue el uso estándar):
Tirantes desnudos :
Tirantes vacíos (NOP):
p.ej
Bloque :
que se convertiría
si se eliminan las llaves, alterando así el flujo de ejecución, no solo el alcance de las variables locales. Por lo tanto, no 'independiente' o 'desnudo'.
Se pueden usar llaves desnudas o un bloque para indicar cualquier sección de código que pueda ser una función (en línea) que desee marcar, pero no refactorizar en ese momento.
fuente
Es una forma artificial de emular a
GOTO
ya que estos dos son prácticamente idénticos:y:
Por otro lado, ambos realmente deberían hacerse con
if
s:Aunque el anidamiento puede volverse un poco feo si solo convierte una larga cadena de reenvíos
GOTO
en anidadosif
. Sin embargo, la respuesta real es una refactorización adecuada, sin imitar construcciones de lenguaje arcaico.Si intentabas desesperadamente transliterar un algoritmo con
GOTO
s, probablemente podrías hacerlo con este modismo. Sin embargo, no es estándar y es un buen indicador de que no te estás apegando a los modismos esperados del idioma.No conozco ningún lenguaje tipo C donde do / while sea una solución idiomática para nada, en realidad.
Probablemente podría refactorizar todo el desastre en algo más sensato para hacerlo más idiomático y mucho más legible.
fuente
do..while(false)
es para correr un número "indefinido" de veces, es para correr una vez .continue
declaración condicional al final como lo mostré. Porundefined
Simplemente significa que usted no sabe si se está ejecutando más de una vez, a menos que se puede predecir si las condiciones serán recibidos en una iteración específica. Sin ningúncontinue
s,do..while(false)
se ejecutará una vez, pero también sin ningúnbreak
s,while(true)
se ejecutará para siempre, por lo que el "comportamiento predeterminado" no es realmente relevante para comprender qué se puede hacer con los bucles.continue
.continue
NO repite el ciclo. "Lacontinue
declaración debe ocurrir solo en una declaración de iteración y hace que el control pase a la porción de continuación del ciclo de la declaración de iteración más pequeña , es decir, al final del ciclo".Algunos codificadores prefieren tener una única salida / retorno 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 tenga un solo retorno.
Soy un programador de Java, así que mi ejemplo sería algo así como
fuente
En tales casos yo uso
fuente
Esto es divertido Probablemente hay interrupciones dentro del ciclo como otros han dicho. Lo hubiera hecho de esta manera:
fuente
do..while(false)
siempre sale,while(true)
es más arriesgado.while(true)
es el idioma correcto en la mayoría de los idiomas. A menudo lo encontrará en aplicaciones GUI como el bucle principal del programa. Debido a que básicamente está asumiendo que el programa no debería morir hasta que se le indique que lo haga, estodo..while(false)
podría causar todo tipo de lógica artificial. Este enfoque puede ser más arriesgado desde un punto de vista perfeccionista, pero es semánticamente más fácil y, por lo tanto, menos propenso a errores para los programadores humanos (lo siento, Skynet).do{...}while(false)
es exactamente igual quewhile(true){ .. break;}
continue
, no son lo mismo.