Jonah Starling
Jonah Starling

Reputation: 543

Inifinite loop when using cin.fail()

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;
}

Upvotes: 0

Views: 473

Answers (2)

edmz
edmz

Reputation: 8494

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.

Upvotes: 6

user3920237
user3920237

Reputation:

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

Upvotes: 1

Related Questions