Exception handling and input from std::cin

So, for my first attempt at exception handling I put this together.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <string>
#include <stdexcept>

int number_from_cin()
{
    int number;
    do {
        try {
            std::cin >> number;
            if (std::cin.good())
                return number;  // Valid number. Return it and exit function.
            else
                throw std::invalid_argument("Invalid argument.");
        }
        catch (std::invalid_argument error) {
            std::cerr << error.what() << std::endl;
            std::cin.clear();
        }
    } while (true);
}

int main()
{
    int number;
    do {
        std::cout << "Enter a number [-100 .. 100]: " << std::flush;
        try {
            number = number_from_cin();
            if ((number < -100) | (number > 100))   // Out of range.
                throw std::out_of_range("Out of range. Try again.");
            else
                break;
        }
        catch (std::out_of_range error) {
            std::cerr << error.what() << std::endl;
        }
        catch (...) {
            std::cerr << "This is not happening..." << std::endl;
            return 1;
        }
    } while (true);
    std::cout << "You chose number " << number << ". I'm impressed!" 
              << std::endl;
    return 0;
}


The program prompts the user for a number between -100 and 100. If the user fails to do so, the out_of_range exception is thrown. If the user fails horribly and writes the name of his dog instead of a number, the invalid_argument exception is thrown. Problem is, the program gets stuck in a infinite loop when that happens. (I believe it might have something to do with the istream (cin), and not the exception handler.) What's going on here?
Wow. I never realized extracting from std::cin sucked this hard. The way I can clear the input buffer is by adding
 
while (std::cin.get() != '\n');
after the call to clear(), or by adding
1
2
std::string temp;
std::cin >> temp;
Not even std::cin.ignore(std::numeric_limits<std::streamoff>::max()) works, for some reason.

Personally, I'd skip this whole mess and just do
1
2
3
4
5
6
7
8
9
10
11
12
13
try {
	std::string line;
	std::getline(std::cin, line);
	std::stringstream stream(line);
	stream >>number;
	if (std::cinstream.good())
		return number;  // Valid number. Return it and exit function.
	else
		throw std::invalid_argument("Invalid argument.");
}
catch (std::invalid_argument error) {
	std::cerr << error.what() << std::endl;
}
Last edited on
std::cin >> number;

If the user writes a character instead of an integer, does that mean that the character is just sitting there, clogging the stream? In the next pass of the loop, the same character is still there. I suppose that's it, right? Because, by running std::cin.get() after the call to clear(), "Invalid argument." is displayed one time for each character + '\n'. (?)

Enter a number [-100 .. 100]: hamburgers
Invalid argument.
Invalid argument.
Invalid argument.
Invalid argument.
Invalid argument.
Invalid argument.
Invalid argument.
Invalid argument.
Invalid argument.
Invalid argument.
Last edited on
If the user writes a character instead of an integer, does that mean that the character is just sitting there, clogging the stream? In the next pass of the loop, the same character is still there. I suppose that's it, right?
Yes.

Because, by running std::cin.get() after the call to clear(), "Invalid argument." is displayed one time for each character + '\n'. (?)
Yes, that's basically what will happen. No, I don't think you should do that.
helios wrote:
Not even std::cin.ignore(std::numeric_limits<std::streamoff>::max()) works, for some reason.

Remember, ignore takes a second argument which is the eof value by default.

@OP: You might find the following thread of interest, although it doesn't deal with exceptions:
http://www.cplusplus.com/forum/beginner/108849/#msg592118
I tried ignore() with nothing, ' ', and '\n'. No dice.
helios wrote:
Wow. I never realized extracting from std::cin sucked this hard.

People have no idea how difficult proper input is.

Never just "clear the input buffer". That implies that you are simply ignoring the user for some (unspecified, irrational) reason.

The trick is to make sure your input routines are properly synchronized with the actual input.

(I'll skip the soapbox lecture about how input handling is mishandled by almost all instructional media.)

To get input from the user, read a string with getline(). Then figure out if it is valid or not.

(Because the user will always press ENTER at the end of every (prompted) input.)

I hate to keep recurring to this post... it's a little dated but the principles remain the same. Check out lines 29 to 41, and observed how to use it in the example main(). http://www.cplusplus.com/forum/beginner/13044/#msg62827

Even if you don't use a fancy functor like that, it is always worth writing one or more routines that are designed to handle one specific type of input -- like getting an integer in a specified range.
http://www.cplusplus.com/forum/beginner/18258/#msg92955
(Notice that this last example suffers from the "123abc" problem, which the first does not.)

Hope this helps.
It's not difficult because it actually is difficult. It's difficult because the interfaces are unusually obtuse.

Never just "clear the input buffer". That implies that you are simply ignoring the user for some (unspecified, irrational) reason.
the user will always press ENTER at the end of every (prompted) input.
Do you expect to ever find a buffer in std::cin after a blocking function has returned that contains more than one input? If the input didn't convert to any of the desired types, then why shouldn't you clear the buffer?
Last edited on
Sorry, I misread to understand that OP wanted to clear input buffer of all input. Only ignore to sync at newline.
Topic archived. No new replies allowed.