2

In order to learn C++, I'm translating a program I wrote in Python.

I wrote this

n = 0
while n < 2:
    try:
        n = int(raw_input('Please insert an integer bigger than 1: '))
    except ValueError:
        print 'ERROR!'

in order to get an integer bigger than 1 from the user.

This is what I wrote in C++ for the moment:

int n = 0;
while (n < 2) {
    cout << "Please insert an integer bigger than 1: ";
    cin >> n;
}

I took a look at try-catch and it seems pretty straight forward. My concern is about how to check the input is an integer. I read about cin.fail() but I couldn't find any official document and I didn't really get how it works.

So, how can I check if the input is integer?

More in general, how can I check if the input is "anything"?

Pigna
  • 2,792
  • 5
  • 29
  • 51

2 Answers2

4

For a situation like this, you'd probably want to read the input as a string, then inspect the string (e.g., "contains only digits, up to a maximum of N digits"). If and only if it passes inspection, parse an int out of it.

It's also possible to combine the inspection and conversion--for example, Boost lexical_cast<int>(your_string) will attempt to parse an int out of the string, and throw an exception if it can't convert the whole thing to an int.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Why not [`std::basic_ios::operator bool`](http://en.cppreference.com/w/cpp/io/basic_ios/operator_bool)? – LogicStuff Feb 09 '16 at 18:14
  • 2
    @LogicStuff testing the stream state only works in certain scenarios. If I input `15.30` in an `int` `cin` will be good but there will still be `.30` left in the stream. – NathanOliver Feb 09 '16 at 18:17
  • @LogicStuff: As Nathan hinted, the stream state tells you whether the input data started with at least one character that could be converted to the right type. In this case, it sounds (at least to me) like he wants to check that *all* the input could be converted instead. – Jerry Coffin Feb 09 '16 at 18:27
  • @JerryCoffin: where is lexical_cast installed? I searched in http://packages.ubuntu.com/search?searchon=contents&keywords=%20boost/lexical_cast.hpp&mode=exactfilename&suite=precise&arch=any but when I tried to install libboost1.46-dev and libboost1.48 I got: `E: Package 'libboost1.46`(and 48)`-dev' has no installation candidate` – Pigna Feb 09 '16 at 19:51
  • @JerryCoffin Oh yeah, but I wanted to avoid installing the whole package – Pigna Feb 09 '16 at 20:55
2

Your Python can code be translated more directly if you use C++11's std::stoi combined with std::getline to read a whole line of input. This is much easier than struggling with standard I/O error handling, which arguably does not have a very user-friendly interface.

std::stoi throws std::invalid_argument if the input could not be correctly parsed as an integer number, and std::out_of_range if the number is too small or too big to fit in an int.

#include <iostream>
#include <string>

int main() {
    int n = 0;
    while (n < 2) {
        std::cout << "Please insert an integer bigger than 1: ";
        std::string input;
        std::getline(std::cin, input);
        try {
            n = std::stoi(input);
        } catch (std::exception const&) {
            std::cerr << "ERROR!\n";
        }
    }
}

If you want to make the code even more similar to its Python equivalent, then you can encapsulate the input in a function:

#include <iostream>
#include <string>

int raw_input(std::string const& message)
{
    std::cout << message;
    std::string input;
    std::getline(std::cin, input);
    return std::stoi(input);
}

int main() {
    int n = 0;
    while (n < 2) {
        try {
            n = raw_input("Please insert an integer bigger than 1: ");
        } catch (std::exception const&) {
            std::cout << "ERROR!\n";
        }
    }
}
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • I have 2 problems: 1. when I run your code I get `error: ‘stoi’ is not a member of ‘std’`, 2. I don't get the meaning of `const& message` inside raw_input and `const&` inside catch. Can you explain please? – Pigna Feb 09 '16 at 18:54
  • 1
    @Pigna: I guess your compiler is too old. Get an upgrade to have C++11 support. `const&` means "const reference". It is common to pass unmodifiable strings like this to avoid needless copies. Such things, however, are handled in introductory C++ books. You need to learn with a book if you are serious about the language. – Christian Hackl Feb 09 '16 at 19:05
  • I didn't need to to upgrade g++, I just had to add `--std=c++11`. I do am serious about it, I've just started studing on learncpp.com. But I prefer to get my hands dirty before finish the course. – Pigna Feb 09 '16 at 19:34
  • Another problem @ChristianHackl : with these method, if I input a float it doesn't throw an error. How to fix this? – Pigna Feb 09 '16 at 19:37
  • 1
    @Pigna: `std::stoi` is quite lenient and will not by default report an error if there are additional characters after a correct integer number has been parsed already (e.g. `"123xxx"` or `"123.001"`). To account for those cases, `std::stoi` has a second parameter which tells you how many characters were parsed to obtain the result. If this number is not equal to the size of the input string, then you know that additional characters are present in the string. `size_t pos; int result = std::stoi(input, &pos); bool error = (pos != input.size());` – Christian Hackl Feb 09 '16 at 21:28