How to break out of a loop from inside a switch?

140,767

Solution 1

Premise

The following code should be considered bad form, regardless of language or desired functionality:

while( true ) {
}

Supporting Arguments

The while( true ) loop is poor form because it:

  • Breaks the implied contract of a while loop.
    • The while loop declaration should explicitly state the only exit condition.
  • Implies that it loops forever.
    • Code within the loop must be read to understand the terminating clause.
    • Loops that repeat forever prevent the user from terminating the program from within the program.
  • Is inefficient.
    • There are multiple loop termination conditions, including checking for "true".
  • Is prone to bugs.
    • Cannot easily determine where to put code that will always execute for each iteration.
  • Leads to unnecessarily complex code.
  • Automatic source code analysis.
    • To find bugs, program complexity analysis, security checks, or automatically derive any other source code behaviour without code execution, specifying the initial breaking condition(s) allows algorithms to determine useful invariants, thereby improving automatic source code analysis metrics.
  • Infinite loops.
    • If everyone always uses while(true) for loops that are not infinite, we lose the ability to concisely communicate when loops actually have no terminating condition. (Arguably, this has already happened, so the point is moot.)

Alternative to "Go To"

The following code is better form:

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

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

Advantages

No flag. No goto. No exception. Easy to change. Easy to read. Easy to fix. Additionally the code:

  1. Isolates the knowledge of the loop's workload from the loop itself.
  2. Allows someone maintaining the code to easily extend the functionality.
  3. Allows multiple terminating conditions to be assigned in one place.
  4. Separates the terminating clause from the code to execute.
  5. Is safer for Nuclear Power plants. ;-)

The second point is important. Without knowing how the code works, if someone asked me to make the main loop let other threads (or processes) have some CPU time, two solutions come to mind:

Option #1

Readily insert the pause:

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

Option #2

Override execute:

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

This code is simpler (thus easier to read) than a loop with an embedded switch. The isValidState method should only determine if the loop should continue. The workhorse of the method should be abstracted into the execute method, which allows subclasses to override the default behaviour (a difficult task using an embedded switch and goto).

Python Example

Contrast the following answer (to a Python question) that was posted on StackOverflow:

  1. Loop forever.
  2. Ask the user to input their choice.
  3. If the user's input is 'restart', continue looping forever.
  4. Otherwise, stop looping forever.
  5. End.
Code
while True: 
    choice = raw_input('What do you want? ')

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

print 'Break!' 

Versus:

  1. Initialize the user's choice.
  2. Loop while the user's choice is the word 'restart'.
  3. Ask the user to input their choice.
  4. End.
Code
choice = 'restart';

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

print 'Break!'

Here, while True results in misleading and overly complex code.

Solution 2

You can use goto.

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

   }
}
exit_loop: ;

Solution 3

An alternate solution is to use the keyword continue in combination with break, i.e.:

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

Use the continue statement to finish each case label where you want the loop to continue and use the break statement to finish case labels that should terminate the loop.

Of course this solution only works if there is no additional code to execute after the switch statement.

Solution 4

A neatish way to do this would be to put this into a function:

int yourfunc() {

    while(true) {

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

    }
}

Optionally (but 'bad practices'): as already suggested you could use a goto, or throw an exception inside the switch.

Solution 5

AFAIK there is no "double break" or similar construct in C++. The closest would be a goto - which, while it has a bad connotation to its name, exists in the language for a reason - as long as it's used carefully and sparingly, it's a viable option.

Share:
140,767
jrharshath
Author by

jrharshath

Updated on July 08, 2022

Comments

  • jrharshath
    jrharshath almost 2 years

    I'm writing some code that looks like this:

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

    Is there any direct way to do that?

    I know I can use a flag, and break from the loop by putting a conditional break just after the switch. I just want to know if C++ has some construct for this already.

    • Tal Pressman
      Tal Pressman over 14 years
      Why do you need a conditional break after the switch? Just change your while from while(true) to while(flag)...
    • Alterlife
      Alterlife over 14 years
      @Dave_Jarvis I assume that this is a simplified version that he has put in here to illustrate what he was trying to do.
    • Jonathan Leffler
      Jonathan Leffler over 14 years
      See: Code Complete (2nd Edn). See also 'Structured Programming with goto statements' by D E Knuth (pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf).
    • Jonathan Leffler
      Jonathan Leffler over 14 years
      @Tal Pressman et al: it looks like it could be a state table machine to me, with one case for each state - including a DONE state in which the code should exit. However, that could perhaps be handled by 'while (msg->state != DONE)' for the loop control.
    • Dave Jarvis
      Dave Jarvis over 14 years
      @Jonathan. Yes, exactly. I would go one further and separate the concerns of the loop's terminating condition from the loop itself (extract it to a method).
    • sbi
      sbi over 14 years
      If you are one of these programmers that produce functions which are several pages long, you'll find goto appealing and, sometimes, the only clean way out. If you tend to organize your code into small functions which are only a few lines long and do one single thing each, you will never run into this problem. (Incidentally your code will be easier to read, too.)
    • sbi
      sbi over 14 years
      @hacker: Well, if you can't see the subway station in front of you due to all the smoke, that advice might not be all that bad. :)
    • Dmitry Vyal
      Dmitry Vyal over 10 years
      Sometimes it's convenient to call return from a case. Not pretty, but works.
    • xcorat
      xcorat over 7 years
      You can always use if-else, unless you have too many cases..!
    • Steven Spark
      Steven Spark over 4 years
      @sbi If you are one of those programmers who only write functions which are only a few lines long, then by definition you are not using/writing (well formatted) switch-case statements ;) (depending on your definition of "few") But yes, you can usually break it up a bit (move the while loop into its own function, use return)... though it won't always make the code more readable. If I grouped every pair of statements into a function then I could write code that reads like a binary tree :) Just try writing readable chunks that belong together so looking up functions doesn't break flow of reading.
    • sbi
      sbi over 4 years
      @Steven Who said I group "every pair of statements into a function"?
    • Steven Spark
      Steven Spark almost 4 years
      @sbi I don't know? Who said it? I for sure didn't say that you did. I just tried to illustrate my point, that shorter doesn't always result more readable code. But it usually does, so it's a good practice to divide code to the shortest possible complete chunks, but not shorter IMO. [ I don't know how to move this conversation off of this comment thread, but I'm curious what you think about libtomcrypt's use of gotos. Do you have an elegant, more readable way of doing it? ]
  • Fragsworth
    Fragsworth over 14 years
    Just don't go wild with that keyword.
  • mmx
    mmx over 14 years
    It's funny I get downvoted because people don't like goto. The OP clearly mentioned without using flags. Can you suggest a better way, considering the OP's limitation? :)
  • dnatoli
    dnatoli over 14 years
    Pretty sure that is the only way to do it but unless you want spaghetti code, I would steer clear of goto.
  • quamrana
    quamrana over 14 years
    +1: goto seems to be an ideal solution here. It would be improved if the while loop were also the sole constituent of a function or method.
  • mmx
    mmx over 14 years
    I don't say it's necessarily the ideal solution. Refactoring the code is probably a better idea. However, I believe it's a valid answer to this question.
  • Clement Herreman
    Clement Herreman over 14 years
    Exception should be used to, well, throw an exception, not a well know behavior of a function.
  • dalle
    dalle over 14 years
    I agree with Afterlife: Put it in a function.
  • Kirill V. Lyadvinsky
    Kirill V. Lyadvinsky over 14 years
    @Mehrdad, I suggested a way in my answer. But it is just for fun :)
  • Michael Krelin - hacker
    Michael Krelin - hacker over 14 years
    upvoted just t to compensate mindless goto haters. I guess Mehrdad knew he's going to lose a couple of points for suggesting sensible solution here.
  • Gorpik
    Gorpik over 14 years
    +1. I understand this is more of a theoretical than a practical question; it clearly asks for a jump instruction. Given that break, continue and return are unsuitable, the only answer is the general jump: goto. This said, while (flag) would be a superior construct, but not what the OP asked for.
  • quamrana
    quamrana over 14 years
    Yeah, but I like exceptions even less ;-)
  • John Carter
    John Carter over 14 years
    Using an exception to emulate a goto is of course worse than actually using a goto! It will probably also be significantly slower.
  • Kirill V. Lyadvinsky
    Kirill V. Lyadvinsky over 14 years
    @Downvoter: It is because I state, that you shouldn't use exception, or because I suggest refactoring?
  • SingleNegationElimination
    SingleNegationElimination over 14 years
    You know I was actually a bit surprised to see a goto answer. I'm glad you did, though, so I don't gotta. +1
  • Jonathan Leffler
    Jonathan Leffler over 14 years
    See: Code Complete (2nd Edn). See also 'Structured Programming with goto statements' by D E Knuth (pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf).
  • Jonathan Leffler
    Jonathan Leffler over 14 years
    Not the down-voter - but... Your answer isn't clear whether you are proposing or condemning the idea of using an exception to exit the loop. Maybe the first sentence should be: "Even if you don't like goto, do not use an exception to exit a loop:"?
  • Paulius
    Paulius over 14 years
    Although the title of this answer may not be the best - I'd say the content is very good and well explained. You'd never see while(true) in any of my code and this post explains all of the reasons why. Up vote from me.
  • Kirill V. Lyadvinsky
    Kirill V. Lyadvinsky over 14 years
    @Jonathan Leffler, Thanks, you showed the sample of valuable comment. updated the answer keeping in mind your comments.
  • sbi
    sbi over 14 years
    The idea that while(true) should be harmful seems bizarre to me. There's loops that check before they execute, there's those that check afterwards, and there's those that check in the middle. Since the latter don't have syntactic construct in C and C++, you'll have to use while(true) or for(;;). If you consider this wrong, you haven't given enough thought to the different kind of loops yet.
  • David Thornley
    David Thornley over 14 years
    Reasons why I disagree: while(true) and for(;;;) are not confusing (unlike do{}while(true)) because they state what they're doing right up front. It is actually easier to look for "break" statements than parse an arbitrary loop condition and look for where it's set, and no harder to prove correctness. The argument about where to put code is just as intractable in the presence of a continue statement, and not much better with if statements. The efficiency argument is positively silly.
  • David Thornley
    David Thornley over 14 years
    This is one of the very few areas I'd consider a goto. It's forward in direction and goes out of scopes rather than in. It implements a very easily understood concept. The problem with goto is that it's easy to use in an unstructured way (and that so few people use it that it's rather obscure). This is, in my opinion, an acceptable and appropriate use.
  • David Thornley
    David Thornley over 14 years
    Throwing an exception is substituting a COMEFROM for a GOTO. Don't do it. The goto works much better.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 14 years
    +1, there is no better way imho, even when considering the OPs limitations. Flags are ugly, break up logic across the whole loop and thus more difficult to grasp.
  • David Thornley
    David Thornley over 14 years
    Whenever you see "while(true)", you can think of a loop that has internal termination conditions, which are no harder than arbitrary end conditions. Simply, a "while(foo)" loop, where foo is a boolean variable set in an arbitrary way, is no better than "while(true)" with breaks. You have to look through the code in both cases. Putting forth a silly reason (and microefficiency is a silly argument) doesn't help your case, by the way.
  • David Thornley
    David Thornley over 14 years
    @Dave Jarvis: We do seem to have some serious differences of opinion. Personally, I don't see that typing /break is any harder than looking for where a sentinel variable changes (and the original example would require a sentinel variable if there was any processing to be done if msg->state == DONE). I looked for efficiencies in the Knuth article, and found stuff that was basically irrelevant (12% improvement from adding a sentinel value) and stuff that was valid 35 years ago. I saw nothing applicable to while(true) at all.
  • David Thornley
    David Thornley over 14 years
    Unfortunately, your memory is faulty. It would be a nice feature, on those rare occasions where it would be useful. (I've seen proposals for number of levels to break from, but those look to me like bugs waiting to happen.)
  • Andrei Vajna II
    Andrei Vajna II over 14 years
    That must have been Java, then. Thanks for answering. And, of course, you're right with the rest, too.
  • sbi
    sbi over 14 years
    While this is indeed very elegant, it has the disadvantage that most developers first have to look at it for a minute in order to understand how/whether this works. :(
  • Mark A. Donohoe
    Mark A. Donohoe about 13 years
    Um... you could be even simpler by putting that condition in the while argument. I mean that's what it's there for! :)
  • unsynchronized
    unsynchronized over 11 years
    at the end of the day, without the goto construct, all languages fail. high level languages obfuscate it, but if you look hard enough under the hood, you will find every loop, or condition boils down to conditional and unconditional branches. we fool ourselves by using for,while,until, switch,if keywords, but eventually they all utilise GOTO (or JMP) in one way or the other. you can delude yourself, inventing all sorts of clever way to hide it, or you can just be pragmatically honest, and use goto, where appropriate, and sparingly.
  • Dan Hulme
    Dan Hulme over 11 years
    All the breaks in that code sample break out of the switch statement.
  • nawfal
    nawfal over 10 years
    The last sentence is what makes this not so great :( Otherwise a very clean implementation.
  • Daniel
    Daniel over 7 years
    @Dave Jarvis: the continue in your python code is totally unnecessary. Initializing choice with some dummy value is the error prone part.
  • Dave Branton
    Dave Branton about 7 years
    Personally, I have never, and will never, use a goto. Unless I'm writing assembly, where it's spelled JMP.
  • Vagish
    Vagish over 5 years
    Single process, single thread bare metal embedded code is always inside while (1).
  • Admin
    Admin almost 5 years
    Probably the person meant without flags, without goto. Morally, there should be a way to break from a loop and switch statement! Why did they use the same keyword????
  • Admin
    Admin almost 5 years
    When you think about it, no computer code is good. We are only trained to understand it. For example xml looked like garbage until I learnt it. Now it is less garbage looking. for(int x=0; x<10; ++x, moveCursor(x,y)) actually caused a logic error in my program. I forgot that the update condition happened at the end.
  • Alex Leibowitz
    Alex Leibowitz almost 5 years
    As a courtesy to people reading this answer, I would avoid commenting on the quality of the good that produced the question and stick to answering the question itself. The question is a variation on the following, which I believe is a feature of many languages: you have a loop nested inside of another loop. You want to break out of the outer loop from the inner loop. How is this done?
  • Askaga
    Askaga over 4 years
    This answer completely ignores the very valid case of a loop with termination condition somewhere in the middle (if that wasn't an appropriate use case we wouldn't even have a break keyword after all) for the sake of demonizing goto. Transforming a while(true) loop with a break in the middle into a while(cond) or do{}while(cond) form would require either an artificial flag or code duplication, both of which are harder to read and argue about. Finally, if you are worried about performance implications of while(true) you can always change it to for(;;)
  • Steven Spark
    Steven Spark over 4 years
    For all the goto haters: opensource.apple.com/source/xnu/xnu-792/libkern/stdio/… , elixir.bootlin.com/linux/latest/source/drivers/usb/core/usb.‌​c github.com/embeddedartists/labtool/blob/master/fw/Lib_USB/… verysource.com/code/30591516_1/usbcore.c.html :) Goto can be useful for freeing up resources after detecting error, zeroing out buffers after crypt (PolarSSL, libtomcrypt)... without expensive exceptions or even C++...
  • Kamil
    Kamil over 3 years
    Neat! I was about to write the same answer.
  • Jean-David Lanz
    Jean-David Lanz over 2 years
    Brilliant in its simplicity. Just what I needed. Thanks!