For finer-grained control of your input routines, you need to learn to use the std::ios_base::iostate stream states goodbit, eofbit, badbit, and failbit
. You can either check the states directly with std::basic_ios::rdstate or use the convenient member functions .good(), .eof(), .bad() and .fail()
, see std::basic_istream.
While checking .good()
will tell you you have good input, it doesn't help you recover from failed input or an error condition or EOF
. For that you have to individually check the error condition that occurred.
Continuing from my comment and adding a bit of detail, you could do something like the following (where you are just doing the same thing for both a
and b
-- which could you could move to a function or member function), e.g.
#include <iostream>
#include <limits>
int main (void) {
int a, b;
/* get input for a */
while (1) /* loop continually reading input */
{
std::cout << "\nenter an integer for a: ";
if (! (std::cin >> a) ) { /* check stream state */
/* if eof() or bad() break read loop */
if (std::cin.eof() || std::cin.bad()) {
std::cerr << "(user canceled or unreconverable error)\n";
goto unrecoverable;
}
else if (std::cin.fail()) { /* if failbit */
std::cerr << "error: invalid input.\n";
std::cin.clear(); /* clear failbit */
/* extract any characters that remain unread */
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
}
}
else { /* on succesful read, just output int and break loop */
std::cout << "integer a: " << a << '\n';
/* extract any characters that remain unread */
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
break;
}
}
/* same thing for b */
while (1) /* loop continually reading input */
{
std::cout << "\nenter an integer for b: ";
if (! (std::cin >> b) ) { /* check stream state */
/* if eof() or bad() break read loop */
if (std::cin.eof() || std::cin.bad()) {
std::cerr << "(user canceled or unreconverable error)\n";
break;
}
else if (std::cin.fail()) { /* if failbit */
std::cerr << "error: invalid input.\n";
std::cin.clear(); /* clear failbit */
/* extract any characters that remain unread */
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
}
}
else { /* on succesful read, just output int and break loop */
std::cout << "integer b: " << b << '\n';
/* extract any characters that remain unread */
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
break;
}
}
unrecoverable:;
}
Note the use of std::numeric_limits<std::streamsize>::max()
used with std::cin.ignore
to clear any offending characters that are left in stdin
. (which essentially extracts up to the maximum streamsize of characters until an '\n'
is found) Necessary so you next input attempt does not attempt to re-read the same bad characters and fail again for the same reason -- generally resulting in an infinite loop if input is taken within a loop.
Also note the use of the good old goto
to allow for a graceful exit from the program regardless whether the user cancels input for a
or b
.
It's an input routine, so after you write it -- go try and break it. If it breaks, fix it. That about the only way you can catch most corner-cases, e.g.
Example Use/Output
$ ./bin/cin_validate_ab
enter an integer for a: no
error: invalid input.
enter an integer for a: OK
error: invalid input.
enter an integer for a: 5, I entered 5!
integer a: 5
enter an integer for b: again!
error: invalid input.
enter an integer for b: 6
integer b: 6
User canceling input with Ctrl+d on Linux (or Ctrl+z on windows).
$ ./bin/cin_validate_ab
enter an integer for a: (user canceled or unreconverable error)
Look thing over and let me know if you have questions.