views:

622

answers:

9

I just started C++ but have some prior knowledge to other languages (vb awhile back unfortunately), but have an odd predicament. I disliked using so many IF statements and wanted to use switch/cases as it seemed cleaner, and I wanted to get in the practice.. But..

Lets say I have the following scenario (theorietical code):

while(1) {

  //Loop can be conditional or 1, I use it alot, for example in my game
  char something;
  std::cout << "Enter something\n -->";
  std::cin  >> something;

  //Switch to read "something"
  switch(something) {
    case 'a':
      cout << "You entered A, which is correct";
      break;
    case 'b':
      cout << "...";
      break;
  }
}

And that's my problem. Lets say I wanted to exit the WHILE loop, It'd require two break statements?

This obviously looks wrong:

case 'a':
  cout << "You entered A, which is correct";
  break;
  break;

So can I only do an IF statement on the 'a' to use break;? Am I missing something really simple?

This would solve a lot of my problems that I have right now.

+5  A: 

Yes, C and C++ have no way to say "exit multiple breakable blocks" (where a "breakable block" is any loop or switch). Workarounds include gotos and use of boolean variables to record whether an outer "breakable block" should also break (neither is elegant, but, that's life).

Alex Martelli
+12  A: 

You could simply have the while loop check for a bool value that is set within one of your case statements.

bool done = false;    
while(!done)
{
 char something;
  std::cout << "Enter something\n -->";
  std::cin  >> something;

  //Switch to read "something"
  switch(something) {
    case 'a':
      cout << "You entered A, which is correct";
      done = true; // exit condition here
      break;
    case 'b':
      cout << "...";
      break;
  }
}
Robb
I'd say this answer is somewhat suboptimal (but not in any way wrong).If code was added under the switch statement, it would be run even though the problem was to exit the outer loop directly.I would have used Dimas if(done) break; method, or a goto.
kigurai
Yea that is a pitfall of this solution, code being executed under that switch. Agreed there.
Robb
funny I always use while ( running )
kenny
Both of you are missing the point. There is nothing wrong with some boilerplate code executing after the done (such as resetting counters or any other misc code). That is not a pitfall but a bonus. You have to think outside the box to see where having the ability to execute code after setting done = true has positive effects. It is not a short-circuit but boolean value
0A0D
+5  A: 

Two break statements will not get you out of the while loop. The first break only gets you out of the switch statement and the second one is never reached.

What you need is to make the condition of the while loop false, assuming that there is nothing in the loop after the switch statement. If there is other code after the switch, you should check the condition after the switch, and break there.


bool done = false;

while(! done)
{
  // do stuff
  switch(something)
  {
    case 'a':
    done = true;  // exit the loop 
    break;
  }

  //  do this if you have other code besides the switch
  if(done)
   break;  // gets you out of the while loop

  // do whatever needs to be done after the switch

}

Dima
why not just do while (true) if you are going to check if (done). This is redundant.
0A0D
it depends... You may or may not want to break immediately after the switch.
Dima
+1  A: 

You can also encapsulate the loop into a function and call return inside the case, for the case that the flag breaking the while is not enough. It is not a good programming practice for some people but if you keep the function simple I don't see why not.

Cristian
A: 

You could change your switch to an ifsystem. It will be compiled to the same thing anyway.

Thomas Ahle
Good argument, I don't mind slightly too-structured code if it doesn't matter. :P
Nullw0rm
Be sure to add a comment explaining why you're using `if/else-if` though, so that you (or someone else) don't see it later and change it to the more obvious switch statement, thereby breaking everything. No pun intended.
Nick Lewis
If somebody refactors your code and doesn't notice that they've changed the fundamental behavior of the method, I'm not sure I would trust them to read the comment in the first place.
Dennis Zickefoose
It might not be compiled to the same thing. `switch` is often implemented using jump tables, which are likely to be faster if there are many `case`s.
Thomas
@Nick Lewis: I think most people would notice the break problem, just like @Jason?@Thomas: I believe compilers would use jump tables as well, if they notice a simple repeated pattern in the else ifs?
Thomas Ahle
+2  A: 

You could try:

  • Using Flags
  • Using Goto
  • Having the Inner Breakable block into a function
  • Using Exceptions
  • Using longjump and setjmp

A topic very similar to this question

http://www.gamedev.net/community/forums/topic.asp?topic_id=385116

mikek3332002
Flags and wrapping the block into a function work. Goto is bad. And exceptions are not at all appropriate here. Exiting the loop is not an error.
Dima
Goto isn't bad. I think goto is appropriate to use in this case, as are flags.
Johannes Schaub - litb
In the example code I'd use goto, it'll keep it tidy if there is code after the switch statement.
mikek3332002
+27  A: 

I would refactor the check into another function.

bool is_correct_answer(char input)
{
    switch(input)
    {
    case 'a':
        cout << "You entered A, which is correct";
        return true;
    case 'b':
        cout << "...";
        return false;
    }
    return false;
}

int main()
{
    char input;
    do
    {
        std::cout << "Enter something\n -->";
        std::cin  >> input;
    } while (!is_correct_answer(input));
}
FredOverflow
This is actually pretty and deserves more upvotes.
Thomas
This should be the answer.
Blessed Geek
One of the functions needs to be modified to account for uppercase or lowercase selections.
Thomas Matthews
Simply change `switch(input)` to `switch(tolower(input))`.
FredOverflow
A: 

You could replace the switch with a slightly over-engineered OO solution...

#include <iostream>
#include <map>
#include <set>

class input_responder
{
    std::set<char> correct_inputs;
    std::map<char, const char*> wrong_inputs;

public:

    input_responder()
    {
        correct_inputs.insert('a');
        wrong_inputs['b'] = "...";
    }

    bool respond(char input) const
    {
        if (correct_inputs.find(input) != correct_inputs.end())
        {
            std::cout << "You entered " << input << ", which is correct\n";
            return true;
        }
        else
        {
            std::map<char, const char*>::const_iterator it = wrong_inputs.find(input);
            if (it != wrong_inputs.end())
            {
                std::cout << it->second << '\n';
            }
            else
            {
                std::cout << "You entered " << input << ", which is wrong\n";
            }
            return false;
        }
    }
};

int main()
{
    const input_responder responder;
    char input;
    do
    {
        std::cout << "Enter something\n -->";
        std::cin  >> input;
    } while (responder.respond(input) == false);
}
StackOOverflow
A: 

You might be interested in the named loop idiom in C++.

#define named(blockname) goto blockname; \
                         blockname##_skip: if (0) \
                         blockname:

#define break(blockname) goto blockname##_skip;

named(outer)
while(1) {

  //Loop can be conditional or 1, I use it alot, for example in my game
  char something;
  std::cout << "Enter something\n -->";
  std::cin  >> something;

  //Switch to read "something"
  switch(something) {
    case 'a':
      cout << "You entered A, which is correct";
      break(outer);
    case 'b':
      cout << "...";
      break(outer);
  }
}
Sumant