¿Cómo salir de un bucle desde el interior de un interruptor?

Estoy escribiendo un código que se ve así:

while(true) { switch(msg->state) { case MSGTYPE: // ... break; // ... more stuff ... case DONE: break; // **HERE, I want to break out of the loop itself** } } 

¿Hay alguna manera directa de hacer eso?

Sé que puedo usar una bandera y salir del ciclo colocando un salto condicional justo después del cambio. Solo quiero saber si C ++ tiene alguna construcción para esto.

Premisa

El siguiente código se debe considerar de mala calidad, independientemente del idioma o la funcionalidad deseada:

 while( true ) { } 

Argumentos de apoyo

El ciclo while( true ) es de forma pobre porque:

  • Rompe el contrato implícito de un ciclo while.
    • La statement while loop debería indicar explícitamente la única condición de salida.
  • Implica que se repite para siempre.
    • El código dentro del ciclo debe leerse para comprender la cláusula de terminación.
    • Los bucles que se repiten para siempre evitan que el usuario finalice el progtwig desde dentro del progtwig.
  • Es ineficiente
    • Hay varias condiciones de terminación de bucle, incluida la comprobación de “verdadero”.
  • Es propenso a errores.
    • No se puede determinar fácilmente dónde colocar el código que siempre se ejecutará para cada iteración.
  • Conduce a un código innecesariamente complejo.

Alternativa a “Ir a”

El siguiente código es mejor forma:

 while( isValidState() ) { execute(); } bool isValidState() { return msg->state != DONE; } 

Ventajas

Sin bandera No goto . Sin excepción. Fácil de cambiar Fácil de leer. Fácil de arreglar Además el código:

  1. Aísla el conocimiento de la carga de trabajo del bucle desde el bucle mismo.
  2. Permite a alguien que mantiene el código extender fácilmente la funcionalidad.
  3. Permite asignar múltiples condiciones de terminación en un solo lugar.
  4. Separa la cláusula de terminación del código para ejecutar.
  5. Es más seguro para las plantas de energía nuclear. 😉

El segundo punto es importante. Sin saber cómo funciona el código, si alguien me pide que haga el ciclo principal para que otros hilos (o procesos) tengan algo de tiempo de CPU, vienen a la mente dos soluciones:

Opción 1

Completamente inserta la pausa:

 while( isValidState() ) { execute(); sleep(); } 

Opcion 2

Sobrescribir ejecutar:

 void execute() { super->execute(); sleep(); } 

Este código es más simple (por lo tanto, más fácil de leer) que un bucle con un switch incorporado. El método isValidState solo debe determinar si el ciclo debe continuar. El caballo de batalla del método debe abstraerse en el método de execute , que permite que las subclases anulen el comportamiento predeterminado (una tarea difícil usando un switch integrado y goto ).

Ejemplo de Python

Contraste la siguiente respuesta (a una pregunta de Python) que se publicó en StackOverflow:

  1. Bucle para siempre.
  2. Pídale al usuario que ingrese su elección.
  3. Si la entrada del usuario es ‘reiniciar’, continúe bucleando para siempre.
  4. De lo contrario, deja de bucle para siempre.
  5. Fin.

Código

 while True: choice = raw_input('What do you want? ') if choice == 'restart': continue else: break print 'Break!' 

Versus:

  1. Inicializa la elección del usuario.
  2. Bucle, mientras que la elección del usuario es la palabra ‘reiniciar’.
  3. Pídale al usuario que ingrese su elección.
  4. Fin.

Código

 choice = 'restart'; while choice == 'restart': choice = raw_input('What do you want? ') print 'Break!' 

Aquí, while True da como resultado un código engañoso y demasiado complejo.

Puedes usar goto .

 while ( ... ) { switch( ... ) { case ...: goto exit_loop; } } exit_loop: ; 

Una solución alternativa es utilizar la palabra clave continue en combinación con la break , es decir:

 for (;;) { switch(msg->state) { case MSGTYPE // code continue; // continue with loop case DONE: break; } break; } 

Use la instrucción continue para terminar cada etiqueta de caso donde desea que el bucle continúe y utilice la statement de break para terminar las tags de casos que deben terminar el ciclo.

Por supuesto, esta solución solo funciona si no hay código adicional para ejecutar después de la instrucción switch.

Una manera ingeniosa de hacer esto sería poner esto en una función:

 int yourfunc() { while(true) { switch(msg->state) { case MSGTYPE: // ... break; // ... more stuff ... case DONE: return; } } } 

Opcionalmente (pero ‘malas prácticas’): como ya se sugirió, podría usar un goto o lanzar una excepción dentro del interruptor.