I am having trouble using the cin method to acquire a variable. When the input is a number there is no problem, but when it is a special character like a dot [.], the whileloop
There are several problems with your code. The first is that you don't
verify that your input has succeeded; the correct condition for the
while
should be:
while ( !cin || (*race < 1 || *race > 3) )
As written, if the input fails (which is what is happening when you
enter a '.'
, supposing that race
has type int*
), then *race
contains its previous value, whatever that was.
The second is that if you do get an error from cin
, you don't clear
it. Once the stream is in an error state, it stays that way until you
explicitly clear it. If cin
has failed, you need to execute:
cin.clear();
somewhere in the loop.
The third is that if cin
fails, you don't extract the character which
made it failed, so that after clearing the error status, you need to
extract it. Given the way you've structured your dialog, you probably
want to ignore everything until the end of the line:
cin.ignore( INT_MAX, '\n' );
You may want to do this even if cin
didn't fail, either in the loop
(if entered because of the *race < 1 || *race > 3
condition), or in
case of success. Alternatively, you may want to shift to reading lines,
and ensure that the line only contains whitespace after the character
you're interested in.
This last solution is the one I would adopt, since it handles pretty much all of the above problems. So my code would look something like:
// return -1 on error in input,
// throw exception on (unexpected) end of file
int
getRace( std::istream& source )
{
std::string line;
if ( !std::getline( source, line ) ) {
throw std::ios_base::failure( "Unexpected end of file" );
}
std::istringstream tmp( line );
int results;
return tmp >> results >> std::ws && tmp.get() == EOF
? results
: -1;
}
// ...
int race = -1;
while ( race < 0 ) {
std::cout << "What is your race\n"
"1. Human\n"
"2. Troll\n"
"3. Zombie\n" << std::flush;
race = getRace( std::cout );
if ( race < 0 ) {
std::cout << "Wrong choice" << std::endl;
}
}
Note that by inputting through a line, you avoid any problems with resetting format errors, skipping erroneous input or resynchronizing in case of error.