0

I'm getting an infinite loop whenever I enter something that can't be converted to integer. Any suggestions?

cout << "Selection: ";
cin >> choice;
while((choice < 0)||(choice > 6)||(cin.fail())) {
    cout << "That isn't a number." << endl;
    cin.clear();
    cout << "Selection: ";
    cin >> choice;
}
Jonah Starling
  • 539
  • 6
  • 20
  • possible duplicate of [How do I flush the cin buffer?](http://stackoverflow.com/questions/257091/how-do-i-flush-the-cin-buffer) – edmz Oct 10 '14 at 15:28

2 Answers2

6

Just clearing is not enough: cin will read that character again and again. You need to ignore it.

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') is a common way to achieve that.


Another way is to read the whole line as string and then parse it (like C#/Java ecc.). In terms of code it could be:

string s;
getline(cin, s);
int n = std::stoi(s);

Notice that std::stoi can throw std::invalid_argument or std::out_of_range. With this method you don't have to ignore anything (thus easier to handle/learn) although you may need to catch possible exceptions.

Take into account that, even though used by other languages, this method is not logically correct: exceptions should be thrown only for exceptional cases (e.g. internal I/O failure). A wrong user input is something you do expect, instead.

edmz
  • 8,220
  • 2
  • 26
  • 45
  • Note that that call (without the second argument) will discard the content of the entire file. I think you meant to use `'\n'` as the second argument. – David G Oct 10 '14 at 15:55
  • I wouldn't recommend `std::stoi` for user input. It makes no sense to have the program terminate with an exception because of invalid input. –  Oct 10 '14 at 15:59
  • @0x499602D2 Although in some cases some might want to ignore up to EOF, it's a good advice in general. Thank you. – edmz Oct 10 '14 at 16:03
  • @black I fail to see how using a try/catch for control flow is more advantageous than a simple while loop. Read [Why not use exceptions as regular flow of control?](https://stackoverflow.com/questions/729379/why-not-use-exceptions-as-regular-flow-of-control) for more. (The top voted answer may be sufficient.) Also C++ is not C# or Java. –  Oct 10 '14 at 16:07
  • @remyabel Well yes, wrong input isn't an exceptional case at all. I've just tried to give an "alternative" and I got inspired by the (wrong) mechanism used by Java/.NET. – edmz Oct 10 '14 at 16:16
1

This is covered in the isocpp.org faq. It recommends using a loop in the form of while (std::cin >> i) so that the loop terminates if extraction fails. Extraction will continue to fail because std::cin does not extract the invalid characters from the input stream. To discard these, you need to use std::cin.ignore. Generally, you can put an arbitrarily large number as the first argument, but it is idiomatic to use std::numeric_limits<std::streamsize>::max() (#include <limits>) instead.

The loop can be succinctly written as:

while(!(std::cin >> choice) || (choice < 0) || (choice > 6)) {
    cout << "Invalid choice." << endl;
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Alternatively, you can put the prompt in the loop condition:

while((std::cout << "Selection: ")
        && (!(std::cin >> choice) || (choice < 0) || (choice > 6))) {
    cout << "Invalid choice." << endl;
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

There is no need for an "invalid character" diagnostic because that is covered by !(std::cin >> choice). Example run:

Selection: a
Invalid choice.
Selection: b
Invalid choice.
Selection: c
Invalid choice.
Selection: d
Invalid choice.
Selection: 15
Invalid choice.
Selection: 10
Invalid choice.
Selection: 5