Как вырваться из цикла изнутри коммутатора?

Я пишу код, который выглядит так:

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

Есть ли какой-либо прямой способ сделать это?

Я знаю, что могу использовать флаг и прерывать цикл, помещая условный разрыв сразу после переключения. Я просто хочу знать, есть ли у С++ какая-то конструкция для этого уже.

Ответ 1

Предпосылка

Следующий код следует считать плохим, независимо от языка или желаемой функциональности:

while( true ) {
}

Поддерживающие аргументы

Цикл while( true ) является плохим, потому что он:

  • Прерывает подразумеваемый контракт цикла while.
    • Объявление цикла while должно явно указывать единственное условие выхода.
  • Предполагает, что он будет навсегда.
    • Код внутри цикла должен быть прочитан, чтобы понять предложение о прекращении.
    • Циклы, которые повторяются навсегда, не позволяют пользователю завершить программу из программы.
  • Неэффективен.
    • Существует несколько условий завершения цикла, включая проверку "true".
  • Является склонным к ошибкам.
    • Невозможно определить, где поставить код, который будет выполняться для каждой итерации.
  • Приводит к излишне сложному коду.

Альтернатива "Перейти к"

Следующий код лучше выглядит:

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

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

Преимущества

Нет флага. Нет goto. Никаких исключений. Легко изменить. Легко читается. Легко исправить. Кроме того, код:

  • Изолирует знание рабочей нагрузки цикла из самого цикла.
  • Позволяет кому-то поддерживать код, чтобы легко расширить функциональность.
  • Позволяет назначать несколько условий завершения в одном месте.
  • Отделяет оканчивающееся предложение от кода для выполнения.
  • Безопаснее для АЭС.; -)

Второй момент важен. Не зная, как работает код, если кто-то попросил меня сделать основной цикл, чтобы другие потоки (или процессы) имели некоторое процессорное время, приходят на ум два решения:

Вариант №1

С готовностью вставьте паузу:

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

Вариант № 2

Заменить выполнение:

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

Этот код проще (поэтому его легче читать), чем цикл со встроенным switch. Метод isValidState должен определять только, должен ли цикл продолжаться. Рабочая лошадка метода должна быть абстрагирована на метод execute, который позволяет подклассам переопределять поведение по умолчанию (сложная задача с использованием встроенных switch и goto).

Пример Python

Контрастируйте следующий ответ (на вопрос Python), который был опубликован в StackOverflow:

  • Петля навсегда.
  • Попросите пользователя ввести свой выбор.
  • Если пользовательский ввод является "перезапуском", продолжайте цикл навсегда.
  • В противном случае остановите цикл навсегда.
  • В конец.
Код
while True: 
    choice = raw_input('What do you want? ')

    if choice == 'restart':
        continue
    else:
        break

print 'Break!' 

Versus:

  • Инициализировать выбор пользователя.
  • Петля, в то время как пользовательский выбор - это слово "перезагрузка".
  • Попросите пользователя ввести свой выбор.
  • В конец.
Код
choice = 'restart';

while choice == 'restart': 
    choice = raw_input('What do you want? ')

print 'Break!'

Здесь while True приводит к вводящему в заблуждение и чрезмерно сложному коду.

Ответ 2

Вы можете использовать goto.

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

   }
}
exit_loop: ;

Ответ 3

Альтернативным решением является использование ключевого слова continue в сочетании с break, то есть:

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

Используйте оператор continue, чтобы завершить каждую метку case, где вы хотите, чтобы цикл продолжался, и используйте оператор break для завершения меток case, которые должны прервать цикл.

Конечно, это решение работает только в том случае, если после инструкции switch нет дополнительного кода.

Ответ 4

Оптимальным способом сделать это было бы включение этого в функцию:

int yourfunc() {

    while(true) {

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

    }
}

Необязательно (но "плохие методы" ): как уже было сказано, вы можете использовать goto или исключать исключение внутри коммутатора.

Ответ 5

AFAIK в С++ отсутствует "двойной разрыв" или аналогичная конструкция. Ближайшим будет goto - который, хотя и имеет плохую коннотацию к его названию, существует на языке по какой-либо причине - если он используется тщательно и экономно, это жизнеспособный вариант.

Ответ 6

Вы можете добавить свой переключатель в отдельную функцию:

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

while(myswitchfunction())
    ;

Ответ 7

Даже если вам не нравится goto, не используйте исключение для выхода из цикла. Следующий пример показывает, насколько он был бы уродлив:

try {
  while ( ... ) {
    switch( ... ) {
      case ...:
        throw 777; // I'm afraid of goto
     }
  }
}
catch ( int )
{
}

Я бы использовал goto, как в ответе this. В этом случае goto сделает код более понятным, чем любой другой вариант. Я надеюсь, что этот будет полезен.

Но я думаю, что использование goto является единственным вариантом здесь из-за строки while(true). Вы должны рассмотреть возможность реорганизации своего цикла. Я бы предположил следующее решение:

bool end_loop = false;
while ( !end_loop ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        end_loop = true; break;
    }
}

Или даже следующее:

while ( msg->state != DONE ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
}

Ответ 8

В этом случае нет конструкции С++ для выхода из цикла.

Используйте флаг для прерывания цикла или (при необходимости) извлеките свой код в функцию и используйте return.

Ответ 9

Вы могли бы использовать goto, но я бы предпочел установить флаг, который останавливает цикл. Затем выйдите из переключателя.

Ответ 10

Почему бы просто не исправить условие в вашем цикле while, из-за чего проблема исчезла?

while(msg->state != DONE)
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        // We can't get here, but for completeness we list it.
        break; // **HERE, I want to break out of the loop itself**
    }
}

Ответ 11

Я думаю:

while(msg->state != mExit) 
{
    switch(msg->state) 
    {
      case MSGTYPE: // ...
         break;
      case DONE:
      //  .. 
      //  ..
      msg->state =mExit;
      break;
    }
}
if (msg->state ==mExit)
     msg->state =DONE;

Ответ 12

Самый простой способ сделать это - поставить простой IF до того, как вы сделаете SWITCH, и что IF проверит ваше условие выхода из цикла.......... так просто, как может быть

Ответ 13

Ключевое слово break в С++ только завершает самую вложенную вложенную итерацию или switch. Таким образом, вы не могли бы вырваться из цикла while (true) непосредственно в инструкции switch; однако вы можете использовать следующий код, который, я думаю, является отличным примером для этого типа проблем:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

Если вам нужно что-то сделать, когда msg->state равно DONE (например, запустить процедуру очистки), затем поместите этот код сразу после цикла for; т.е. если у вас есть:

while (true) {
    switch (msg->state) {
    case MSGTYPE:
        //... 
        break;

    //...

    case DONE:
        do_cleanup();
        break;
    }

    if (msg->state == DONE)
        break;

    msg = next_message();
}

Затем используйте вместо этого:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

assert(msg->state == DONE);
do_cleanup();

Ответ 14

Меня поражает, насколько просто это рассматривает глубину объяснений... Здесь все, что вам нужно...

bool imLoopin = true;

while(imLoopin) {

    switch(msg->state) {

        case MSGTYPE: // ... 
            break;

        // ... more stuff ...

        case DONE:
            imLoopin = false;
            break;

    }

}

LOL!! В самом деле! Это все, что вам нужно! Одна дополнительная переменная!

Ответ 15

while(MyCondition) {
switch(msg->state) {
case MSGTYPE: // ... 
    break;
// ... more stuff ...
case DONE:
   MyCondition=false; // just add this code and you will be out of loop.
    break; // **HERE, you want to break out of the loop itself**
}
}

Ответ 16

У меня такая же проблема и решена с использованием флага.

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

Ответ 17

Нет, у С++ нет конструкции для этого, поскольку ключевое слово "break" уже зарезервировано для выхода из блока переключателя. В качестве альтернативы может существовать do..while() с флагом выхода.

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);

Ответ 18

Если я хорошо помню синтаксис С++, вы можете добавить ярлык к операторам break, как и для goto. Так что вы хотите легко написать:

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

outofloop:
// rest of your code here

Ответ 19

  while(true)
  {
    switch(x)
    {
     case 1:
     {
      break;
     }
    break;
   case 2:
    //some code here
   break;
  default:
  //some code here
  }
}