4

I ask the user for an integer input and I do not want to execute code unless it is strictly an integer.

int x;
if(cin >> x)

For instance if the user inputs a double above, the if statement will execute with implicit conversion to an integer. Instead I don't want the code to execute at all.

How can I prevent this?

Mars
  • 4,677
  • 8
  • 43
  • 65

2 Answers2

10

There is no conversion there. If the user enters a fraction (there is no double), then the >> extraction stops at the decimal point.

http://ideone.com/azdOrO

int main() {
    int x;
    std::cin >> x;
    std::cout << std::cin.rdbuf();
}

 input:

123.456

output:

.456

If you want to flag the existence of the decimal point as an error, you will have to do something to extract it from cin and detect it.

One good parsing strategy with C++ streams is to getline what you know you will process into an istringstream, call it s, then check that that s.peek() == std::char_traits<char>::eof() when you finish. If you don't use getline to pull the individual number, then peek can check whether the next character is a space (using std::isspace) without consuming that character from the stream.

Probably the cleanest way to check that input is finished, although it's somewhat esoteric, is to use std::istream::sentry.

if ( ! ( std::cin >> x ) || std::istream::sentry( std::cin ) ) {
    std::cerr << "Invalid or excessive input.\n";
}

This consumes space at the end of the input. sentry also provides a noskipws option to avoid consuming the space.

if ( ! ( std::cin >> x ) || std::istream::sentry( std::cin, true ) ) {
    std::cerr << "Invalid or excessive input. (No space allowed at end!)\n";
}
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • +1 checking the `rdbuf()` for additional containment that is not whitspace (empty would, by default, be ok) is a splendid work-around. – WhozCraig Aug 27 '13 at 02:22
0

This seems to work. It ignores whitespace, I don't know if that's ok with you.

string s;
cin >> s;
stringstream ss(s);
int x;
if (! (ss >> x))
{
    cerr << "You didn't enter an integer." << endl;
    return -1;
}
string temp;
ss >> temp;
if (! temp.empty())
{
    cerr << "You didn't enter an integer." << endl;
    return -1;
}
Neil Kirk
  • 21,327
  • 9
  • 53
  • 91
  • 1
    `if (! (ss >> x) || ss.peek() != stringstream::traits::eof() )` would implement my suggestion, and be more DRY. – Potatoswatter Aug 27 '13 at 02:35
  • @Potatoswatter What do you mean by DRY? – Neil Kirk Aug 27 '13 at 02:36
  • "Don't Repeat Yourself", a programming principle. – Potatoswatter Aug 27 '13 at 02:37
  • @Potatoswatter Is it correct that either condition of || may be checked first? – Neil Kirk Aug 27 '13 at 02:40
  • No, `||` sequences its operands. All side-effects of the left hand side are complete before the right-hand side begins. Unless it's overloaded, which would just be evil. (But that can't apply here.) – Potatoswatter Aug 27 '13 at 02:44
  • @Potatoswatter: But if there is white space after the integer I would still consider it valid. So I prefer `char c;if (!(ss >> x) || (ss >> c)) { /*Fail*/ }` – Martin York Aug 27 '13 at 02:59
  • @LokiAstari In this example, there cannot be trailing whitespace because the stringstream was initialized by the extractor of `std::string`. Another strategy not requiring a dummy temporary and allowing whitespace would be `|| std::istream::sentry( ss )`. This is probably cleaner in every way, because you can also pass `(ss, true)` to *not* tolerate whitespace. – Potatoswatter Aug 27 '13 at 03:10