3

I can't seem to figure out why this falls into a loop after getting non-int input. I've tried cin.flush(), which doesn't seem to exist, cin.clear(), which seems like it should work, even cin.sync() after reading someone else post about it working, but didn't seem to make much sense. Also tried cin.bad().

Thank you very much for any help

Please enter the first number: f Sorry, I don't think that's a number?

Please enter the first number: Sorry, I don't think that's a number?

Please enter the first number: Sorry, I don't think that's a number?

Please enter the first number: Sorry, I don't think that's a number?

Please enter the first number: Sorry, I don't think that's a number?Sorry, you d on't get any more tries. Press any key to continue . . .

#include <iostream>
using namespace std;

int main(){
    int entry;
    int attempts = 1;
    int result;
    while(attempts <= 5) {
        try {
            cout << "\n\nPlease enter the first number: ";
            cin >> entry;
            if (cin.fail())
                throw "Sorry, I don't think that's a number?";
            if (entry < 0)
                throw "Sorry, no negative numbers. Try something else? ";
            cout << "\nNow the second number: ";
            cin >> entry;
            cin.clear();
            cin.get();
        }
        catch (char* error) {
            cout << error;
            attempts++;
        }
    }
    if (attempts > 5)
        cout << "Sorry, you don\'t get any more tries.\n";
    system("pause");
    return 0;
}
Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
na.
  • 33
  • 1
  • 1
  • 3
  • 1
    As others have said, your exception use is inappropriate. Replace `throw` with "std::cerr << " and then `continue;`. Also, use a for-loop: `for (int attempts = 1; attempts <= 5; +attempts)`. – GManNickG Feb 18 '10 at 21:32
  • possible duplicate of [infinite loop with cin](http://stackoverflow.com/questions/5864540/infinite-loop-with-cin) – sashoalm Feb 09 '15 at 17:03

6 Answers6

9

You should think carefully what you want to do if user gives invalid input in this case. Usually in these cases the best solution is to read one line from the input and throw it away.

Try putting cin.clear() and std::cin.ignore(std::numeric_limits<streamsize>::max(),'\n'); in your catch clause. cin.clear() clears the failure state in cin, and cin.ignore() throws away rest of the line waiting in the input buffer.

(And yes, you probably should rethink your use of exceptions).

hrnt
  • 9,882
  • 2
  • 31
  • 38
3

The most straight-forward (but not usually the easiest nor the fastest) way of dealing with validation of line-based input is to always read it line at a time. This way no extra whitespace (such as linefeed characters) is left unread in the buffer in any case, and discarding erroneous input is also quite automatic.

// Infinite loop for retrying until successful
while (true) {
    // Ask the user something
    std::cout << prompt;
    // Read the answer (one full line)
    std::string line;
    if (!std::getline(std::cin, line))
      throw std::runtime_error("End of input while expecting a value");
    // Put the line read into iss for further parsing
    std::istringstream iss(line);
    int val;
    // Read val from iss and verify that reading was successful and
    // that all input was consumed
    if (iss >> val && iss.get() == EOF) return val;
    std::cout << "Invalid input, try again!\n";
}

It is fun to make a BASIC style input function out of this:

template <typename Val> void input(std::string const& prompt, Val& val) {
    // (the above code goes here, slightly adjusted)
}

int main() {
    int w;
    double l;
    input("Enter weight in kg: ", w);
    input("Enter length in m: ", l);
    std::cout << "BMI: " << w / (l * l) << std::endl;
}

Notes for the pedantics who were going to downvote me:

  • function input should be specialized for std::string
  • exceptions thrown by the input function should be caught in main
Tronic
  • 10,250
  • 2
  • 41
  • 53
  • I adapted your solution to a problem I had in one of my classes. Thanks! :) (yes, I put a link to your comment in the source code) – Carlos Agarie Apr 11 '13 at 03:31
2

My Problem was to block char input to a cin >> number

This error caused an 'infinite' loop showing my prompt cout << prompt with no way of exit but kill the process ...

The following shows what worked for me!

========================================

double fi_trap_d()    // function to return a valid range double catching errors  
{  
  double fi_game_sec;  
//-------------------------------------------  
   do  
   {  
    fi_game_sec = -1;  
    cout << fi_game_sec_c;  

        //------------------------------  
    cin.ignore();  // (1)
        //------------------------------  

    try  
    {  cin >> fi_game_sec;  cin.clear(); }  // (2)
        catch (...)  //out_of_range)  
      {  
        fi_game_sec = -1;  
        cout << " Dis am an error!\n";  
                     // just loop back as we asked for a number  
      }  
    } while (fi_game_sec < 1);  
//-------------------------------------------  
  return fi_game_sec;  
}  

========================================

Despite trying the " Dis am an error! " has NEVER shown up.

The key was (1) & (2) !

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
0

Exceptions should be used to handle exceptional, unexpected situations. Incorrect input from a user is neither unexpected nor exceptional -- it's more or less the norm. Personally, I tend to just ignore most bad input completely (when it can't be prevented). When (and only when) they enter something unusable repeatedly is it even worth pointing it out to them. As such, I'd tend to write the code something like:

char ch;
int attempts = 0;

std::cout << "Please enter the first number: ";
do { 
    cin >> ch;
    attempts++;
    if (attempts > 5)
        std::cerr << "The only allowable inputs are '0' through '9'\n";            
} while (cin.good() && !isdigit(ch));
int first_number = ch - '0';

This reads the input as a character, so it's always removed from the input stream. Then it attempts to validate the input, and if it fails, attempts to read again. Of course, you might want/need to get a little more elaborate, such as reading an entire line, attempting to convert it to a number, and reading another line if that fails.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Reading characters is a very bad way to read numbers (that presumably can be more than one digit). – Tronic Feb 18 '10 at 21:30
  • @Tronic:Which, of course, is why I said you might want to read a whole line and attempt to convert that. The reason for doing it, however, is that (at least normally) anything the user can enter will be read into chars successfully, so they don't stay in the input buffer, causing problems like the OP is asking about. – Jerry Coffin Feb 18 '10 at 21:38
-1

Why are you doing this with exceptions? You're not going to kill the program on input, so you shouldn't throw an exception.

Just print out your error message and attempt to read in again.

Liz Albin
  • 1,479
  • 8
  • 8
  • -1. Ignores the issue at hand, which was why the code "falls into a loop after getting non-int input." Printing the error immediately and using `continue` would yield the same results. – Rob Kennedy Feb 18 '10 at 21:31
-1

It looks like you would be better off with iostream's native exceptions. Enable with

cin.exceptions( ios::failbit );

try {
   …
} catch( ios_base::failure & ) {
  cin.clear();
  …
}

Never, ever throw an an object not derived from std::exception, and especially not a native type like char*.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Except if you specifically want to signal something so that it doesn't get caught in the error handling (the interruption support of the proposed thread API of C++0x used this trick to interrupt a thread). – Tronic Feb 18 '10 at 21:27
  • -1. Making the proposed change wouldn't solve the problem at hand, which has nothing to do with what type of exception is thrown or whether the exception is thrown manually or automatically by the stream. – Rob Kennedy Feb 18 '10 at 21:37
  • @Rob: But I *did* address the problem at hand in the example code… – Potatoswatter Feb 18 '10 at 23:02
  • No you didn't. The call to `clear` resets the error state, but the bad input remains in the buffer. – Rob Kennedy Feb 19 '10 at 01:18