views:

1919

answers:

11

Hi,

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.

Thanks,
jrh

+41  A: 

You can use goto.

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

   }
}
exit_loop: ;
Mehrdad Afshari
Just don't go wild with that keyword.
Fragsworth
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? :)
Mehrdad Afshari
Pretty sure that is the only way to do it but unless you want spaghetti code, I would steer clear of goto.
link664
+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.
quamrana
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.
Mehrdad Afshari
@Mehrdad, I suggested a way in my answer. But it is just for fun :)
Kirill V. Lyadvinsky
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.
Michael Krelin - hacker
+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.
Gorpik
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
TokenMacGuy
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
+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.
Johannes Schaub - litb
+1  A: 

You could potentially use goto.

But I would prefer to set a flag that stops the loop.
Then break out of the switch.

Martin York
+7  A: 

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 quite a viable option.

Amber
+12  A: 

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.

Alterlife
Exception should be used to, well, throw an exception, not a well know behavior of a function.
Clement Herreman
I agree with Afterlife: Put it in a function.
dalle
Throwing an exception is really evil in this case. It means "I want to use a goto, but I read somewhere that I should not use them, so I'll go with a subterfuge and pretend to look smart".
Gorpik
No. while(true) is the culprit; breaking out of the loop anywhere other than the condition of the while loop is poor style.
Dave Jarvis
I agree that throwing an exception or using a goto is a terrible idea, but they are working options and have been listed as such in my answer.
Alterlife
Throwing an exception is substituting a COMEFROM for a GOTO. Don't do it. The goto works much better.
David Thornley
+3  A: 

There's no C++ construct for breaking out of the loop in this case.

Either use a flag to interrupt the loop or (if appropriate) extract your code into a function and use return.

sharptooth
+27  A: 

while( true )

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

while( true ) {
}

Reasons

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.
  • Is more difficult to prove as correct.

Alternative to Goto

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).

Dave Jarvis
“Considered Harmful” Essays Considered Harmful - http://meyerweb.com/eric/comment/chech.html
Kobi
See: Code Complete (2nd Edn). See also 'Structured Programming with goto statements' by D E Knuth (http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf).
Jonathan Leffler
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.
Paulius Maruška
+1 from me as well. Neatly explained.
Alterlife
@Jonathan. (1) Smacks of an appeal to authority argument. (2) Was written in the 1970s, before OO started to shine. I will disagree with Knuth's article on this one -- the examples he wrote lend themselves well enough to C, but this is C++ and OO techniques (such as extensibility) cannot be ignored.
Dave Jarvis
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.
sbi
thanks for the great explanation :)
Here Be Wolves
@sbi. On the contrary, for(;;) is poor form for the same reasons listed above. Rather than saying "you haven't given enough thought", perhaps you could refute the reasons listed in the answer?
Dave Jarvis
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
@Thornley. So every time I see while(true) in your code, I can presume the loop never terminates? Because that's what "while(true)" states "right up front". That you have to look through the code to understand the terminating condition is a problem -- break and return statements could be additional terminating clauses. Calling a reason silly does not refute it, by the way.
Dave Jarvis
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
@Thornley. We'll have to agree to disagree. I prefer to see methods that exit from one spot and loops that terminate at one spot. I strongly oppose having to scan through reams of code to be sure that I understand the exit or terminating condition(s): it means I have to wholly comprehend (and logically retrace the code), thus increasing the probability of introducing errors. As for microefficiency, Knuth, at risk of appeal to authority, argued for 13% and 3% efficiencies in the article listed by Jonathan.
Dave Jarvis
@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
@Dave: I gave a reason: It's the only way to get a loop that checks the condition in the middle. Classic example: `while(true) {string line; std::getline(is,line); if(!is) break; lines.push_back(line);}` Of course I could transform this to a preconditioned loop (using `std::getline` as the loop condition), but this has disadvantages on its own (`line` would have to be a variable outside the loop's scope). And if I did, I would have to ask: Why do we have three loops anyway? We could always transform everything into a preconditioned loop. Use what fits best. If `while(true)` fits, then use it.
sbi
+5  A: 

Even if you don't like goto, do not use an exception to exit a loop. The following sample shows how ugly it could be:

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

I would use goto as in this answer. In this case goto will make code more clear then any other option. I hope that this question will be helpful.

But I think that using goto is the only option here because of the string while(true). You should consider refactoring of your loop. I'd suppose the following solution:

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

Or even the following:

while ( msg->state != DONE ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
}
Kirill V. Lyadvinsky
Yeah, but **I** like exceptions even less ;-)
quamrana
Using an exception to emulate a goto is of course worse than actually using a goto! It will probably also be significantly slower.
therefromhere
@Downvoter: It is because I state, that you shouldn't use exception, or because I suggest refactoring?
Kirill V. Lyadvinsky
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:"?
Jonathan Leffler
@Jonathan Leffler, Thanks, you showed the sample of valuable comment. updated the answer keeping in mind your comments.
Kirill V. Lyadvinsky
+4  A: 

You could put your switch into a separate function like this:

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())
    ;
Adam Pierce
+10  A: 

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.

sakra
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. `:(`
sbi
A: 

If I remember C++ syntax well, you can add a label to break statements, just like for goto. So what you want would be easily written:

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
Andrei Vajna II
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.)
David Thornley
That must have been Java, then. Thanks for answering. And, of course, you're right with the rest, too.
Andrei Vajna II
A: 

The simplest way to do it is to put a simple IF before you do the SWITCH , and that IF test your condition for exiting the loop .......... as simple as it can be

SpiriAd