Ashley Davies
Ashley Davies

Reputation: 1893

How do I prevent a runaway input loop when I request a number but the user enters a non-number?

I need to know how to make my cin statement not appear to 'remove' itself if you input the wrong type. The code is here:

int mathOperator()
{
  using namespace std;

  int Input;
  do
  {
    cout << "Choose: ";
    el();
    cout << "1) Addition";
    el();
    cout << "2) Subtraction";
    el();
    cout << "3) Multiplication";
    el();
    cout << "4) Division";
    el();
    el();
    cin >> Input;

  }
  while (Input != 1 && Input != 2 && Input!=3 && Input!=4);
  return Input;
}

Execute, enter, for example, a character, and it loops nonstop acting as though the cin statement isn't there.

Upvotes: 3

Views: 3565

Answers (6)

CashCow
CashCow

Reputation: 31445

After reading in a bad value, cin is in a "failed" state. You have to reset this.

You must both clear the error flag and empty the buffer. thus:

   cin.clear(); 
   cin.ignore(std::numeric_limits<streamsize>::max(), '\n');

The second call "flushes" the input buffer of any data that might be there, to get you ready for the next "cin" call.

If you find yourself writing these 2 lines "all over your code" you could write a simple inline function to replace it.

   inline void reset( std::istream & is )
   {
       is.clear();
       is.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
   }

Although I have made this function take any istream, most of the time it would only be used for cin where a user is entering and enters something invalid. If it's an invalid file or stringstream input, there is no way to fix it and you would do best to just throw an exception.

Upvotes: 2

user467871
user467871

Reputation:

char Input;

 do
 {
// same code 
 }
 while (Input != '1' && Input != '2' && Input != '3' && Input!='4');
 return Input;

[EDIT]

If you want convert char to int you can use this piece of code

int i = (Input - 48);

Upvotes: 2

Jerry Coffin
Jerry Coffin

Reputation: 490218

Unless you're quite certain about the input being in the proper format, you rarely want to use operator>> directly from the input stream.

It's usually easier to read a line with std::getline, put that into a std::istringstream, and read from there. If that fails, you print/log an error message, throw away the remainder of the line and (possibly) go on to the next line.

Upvotes: 3

Fred Nurk
Fred Nurk

Reputation: 14212

You must check that input succeeded and handle when it doesn't:

int mathOperator() {
  using namespace std;

  int Input;
  do {
    cout << "Choose: ";
    el();
    cout << "1) Addition";
    el();
    cout << "2) Subtraction";
    el();
    cout << "3) Multiplication";
    el();
    cout << "4) Division";
    el();
    el();
    while (!(cin >> Input)) {  // failed to extract
      if (cin.eof()) {  // testing eof() *after* failure detected
        throw std::runtime_error("unexpected EOF on stdin");
      }
      cin.clear();  // clear stream state
      cin.ignore(INT_MAX, '\n');  // ignore rest of line
      cout << "Input error.  Try again!\n";
    }
  } while (Input != 1 && Input != 2 && Input!=3 && Input!=4);
  return Input;
}

If you don't check that extraction succeeded, then cin is left in a failed state (cin.fail()). Once in a failed state, later extractions will immediately return instead of trying to read from the stream, effectively making them no-ops – leading to your infinite loop.

Upvotes: 5

Michael
Michael

Reputation: 1622

I agree that a char is just as handy, since you can always cast to int, to answer your question as to why this is happening, when a cin input is exected as an int but a char is entered, the input is kept in the input stream for the duration of the loop, which is why it seems to "disappear."

For more information: see the post from Narue at http://www.daniweb.com/forums/thread11505.html

Upvotes: 0

Andriy Tylychko
Andriy Tylychko

Reputation: 16266

don't read int, read char so cin will pass any invalid character

Upvotes: 2

Related Questions