Rather than complain about stuff that
is perfectly valid, why not address the OP's problem?
You can do as
satm2008 suggests to just get input as an
int, or you can overload the extraction operator to work with a
choice.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
// Menu Chooser rewrite, p70-1
// Would demonstrate the switch statement and enumeration if I had a clue
#include <iostream>
#include <limits>
using namespace std;
// Here the 'choice' type is declared globally, so we can provide an extraction operator for it
enum choice {easy = 1, normal, hard};
// And here is the operator itself.
// This is actually a very simple version, but it complies to your program's specification.
// You could add the power to enter "easy", etc as strings also..
// Whatever you like is possible.
//
// Notice how if anything goes wrong, one of the fail bits gets set and the target is not modified.
// That is how all extraction operators should work.
//
istream& operator >> ( istream& ins, choice& c )
{
int i;
if (ins >> i)
{
// (See the notes following this [code] block for more about the assumptions made here)
if ((i < easy) || (i > hard)) ins.setstate( ios::badbit );
else c = (choice)i;
}
return ins;
}
int main()
{
// (Don't forget to apply some indentation to your source code.)
cout << "Difficulty Levels\n\n";
cout << "1 - Easy\n";
cout << "2 - Normal\n";
cout << "3 - Hard\n\n";
choice level;
cout << "Choice: ";
cin >> level;
if (!cin.good())
{
// If the user entered something wrong, make sure 'level' has a bad
// value. (More on this in the notes that follow.)
level = (int)hard +1;
// And reset the input stream so that it can work again. The 'ignore'
// that follows is important, but since we do it either way it is not
// necessary to put it inside this block.
cin.clear();
}
// Since reading a 'choice' doesn't read the ENTER that the user pressed
// after typing his selection, we need to get rid of it. (And any erroneous input.)
cin.ignore( numeric_limits <streamsize> ::max(), '\n' );
// Having gone through the trouble of creating an enum for
// your values, you might as well use them:
switch (level)
{
case easy:
cout << "You picked Easy." << endl;
break;
case normal:
cout << "You picked Normal." << endl;
break;
case hard:
cout << "You picked Hard." << endl;
break;
default:
cout << "You made an illegal choice." << endl;
}
// (See the notes that follow about the numeric limits thing)
cout << "Press ENTER to continue...";
cin.ignore( numeric_limits <streamsize> ::max(), '\n' );
return 0;
}
|
Note 1: Enumerations
The purpose of an enumeration is to abstract away numeric constants into a friendly, human-readable name. Once the enum is defined the programmer shouldn't have to concern himself with the
actual values of its parts.
In your program, you are treating the enum as if it were an integer --meaning that you still care about its values. Don't.
The extraction operator happens to know that
choice is actually an enum, but that is only because of convenience, since the user's input directly matches the enum value. This is one place where that is fine, but again, you could easily make it properly abstract.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
istream& operator >> ( istream& ins, choice& c )
{
int i;
ins >> i;
if (ins)
switch (i)
{
case 1: c = easy; break;
case 2: c = normal; break;
case 3: c = hard; break;
default: ins.setstate( ios::badbit );
}
return ins;
}
|
In your
switch across the choices, you have a select
case for every possible choice, and then a default. But technically there shouldn't be any bad values. I manufactured one by assigning it an improper value (line 48), but if an enumeration is expected to have bad values then one should be explicitly defined:
enum choice {nolevel, easy, normal, hard};
Now you can use it and just automatically handle
cin for potential failure.
1 2 3 4 5 6
|
choice level = nolevel;
cin >> level;
cin.clear();
cin.ignore( ... );
|
Or:
1 2 3 4 5 6
|
choice level;
if (!(cin >> level)) level = nolevel;
cin.clear();
cin.ignore( ... );
|
Note 2: stream sizes
Unfortunately,
in_avail() is not very useful. It only tells you if there is anything sitting in the read buffer. It doesn't actually tell you if there is more input waiting to be read.
Further, it is not defined on
cin, and different implementations are notoriously spotty about how they implement it. For example,
MinGW will always return 0, no matter what.
Instead of messing with that, just ignore everything up to and including the next ENTER keypress (which is translated into a newline '\n'). If the input really has terminated then it will still behave properly.
The most general way is to ignore a maximum of 'as many characters are possible in an input stream', in other words,
numeric_limits<streamsize>::max()
Hope this helps.