2

I'm trying to find a way to keep asking for user input even after an invalid input is taken by using std::cin.fail(). However, when the input is invalid, the program simply runs in a loop and does not pause on std::cin even with std::cin.ignore().

I tried moving std::cin.ignore() to before the std::cin >> input and although it does work, the program first stops at the std::cin.ignore() line (requiring an input first that won't be used) before going to the std::cin input line which is not what I want(I want it to immediately ask for input).

Is there a reason why and how do I fix it?

double input;
do {
    std::cin.clear(); 
    std::cout << "Enter a double value : ";
    std::cin >> input;
    if (std::cin.fail()) {
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "Invalid input!\n";
    }   
} while (std::cin.fail());
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
aloof
  • 23
  • 5

1 Answers1

3

You must add the line

std::cin.clear(); 

immediately before the line

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

otherwise the call to std::cin.ignore will fail, because the stream is in a failed state.

However, this will cause the loop condition of the outer loop to not work. Therefore, the entire code should be changed to the following:

double input;

while( true ) {
    std::cout << "Enter a double value : ";
    std::cin >> input;
    if ( std::cin.fail() ) {
        std::cin.clear();
        std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
        std::cout << "Invalid input!\n";
        continue;
    }

    //input is ok, so break out of infinite loop
    break;
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • Thank you! That solved my problem. As a follow up though, if I were to input "123abc", this would be considered as valid input and the program would take "123" as the input. If I wanted the program to instead take this as an invalid input, would accepting a string input be better and writing logic to parse through that input be the "correct" method? – aloof Dec 23 '22 at 07:17
  • 1
    @aloof Absolutely, that is the best way – john Dec 23 '22 at 07:18
  • 1
    @aloof: If you want `123abc` to be rejected, then you should not call `std::cin.ignore`, but should instead read the rest of the line as a string, for example using [`std::getline`](https://en.cppreference.com/w/cpp/string/basic_string/getline), so that you can inspect it. You will probably want to only allow whitespace characters in that string, which can be checked for by calling [`std::isspace`](https://en.cppreference.com/w/c/string/byte/isspace). – Andreas Wenzel Dec 23 '22 at 07:27
  • 1
    @aloof: However, in my opinion, instead of using `operator >>` for input, it is generally better to always read an entire line of input at once as a string. You can then attempt to convert the string to a number using `std::stod`. See [this answer of mine](https://stackoverflow.com/a/72316364) for an example for integers, which are converted using `std::stoi`. The program in my answer will also reject input such as `123abc`. That program would only require minor modifications to use floating-point numbers instead of integers (for example it should use `std::stod` instead of `std::stoi`). – Andreas Wenzel Dec 23 '22 at 07:36
  • Thanks for the suggestions! I’ll be sure to explore both :) – aloof Dec 23 '22 at 08:25