-3

I am not understanding Try, throw, catch statements and am wondering how would be the best why to catch a char when all in puts in your code should be int. It is to help stop people form being silly and putting an "a" when I ask for their favorite number. Here is an example of my code i am trying to protect from someone entering a char when i want ints:

int a, b;
std::cout << "Enter a Numerator: ";
std::cin >> a;
std::cout << "Enter a Denominator: ";
std::cin >> b;
Eldren
  • 9
  • 1
  • To protect from what, exactly? – Nicol Bolas Feb 01 '19 at 06:12
  • Possible duplicate of [How to throw a C++ exception](https://stackoverflow.com/questions/8480640/how-to-throw-a-c-exception) – Duck Dodgers Feb 01 '19 at 06:13
  • 1
    I'll saw this forever. Users type characters and strings, not integers. Read the input as a string and do your own validation. – selbie Feb 01 '19 at 06:14
  • 1
    or better look at [this](https://stackoverflow.com/questions/24504582/how-to-test-whether-stringstream-operator-has-parsed-a-bad-type-and-skip-it). – Duck Dodgers Feb 01 '19 at 06:15
  • 1
    When talking about `cin`, the best way to handle errors is `if (!(std::cin >> a))` then check if and *unrecoverable* error occurred, e.g. `eofbit` or `badbit` is set with `if (std::cin.eof() || std::cin.bad())` or whether a *recoverable* error occurred, e.g. `failbit` is set, checking with `else if (std::cin.fail())` after which `std::cin.clear();` can be called to clear `failbit`. – David C. Rankin Feb 01 '19 at 06:22
  • Don't use exceptions for normal flow control. – Retired Ninja Feb 01 '19 at 06:29

2 Answers2

2

If you merge the two answers from the two questions (this and this), I referred to in the comments section of your question, you would get this,

#include <iostream>

int main()
{
    int a, b;
    std::cout << "Enter a Numerator: ";
    std::cin >> a;
    std::cout << "Enter a Denominator: ";
    std::cin >> b;

    if (!std::cin.good())
    {
        throw std::invalid_argument( "received strings. Need integers" );
    }
}

As the first linked answer mentions, you can refer here for more on what exceptions to throw.

And as the second linked answer mentions, "cin will toggle it's failbit if the user does not enter a correct data type that it was expecting. Changing the datatype of the inputs to ints and checking this failbit will allow you to validate user input." .

Just as an after thought, you better check that denominator for zero also, with something like this added to the above code example.

if (b /*assuming b is denominator*/ == 0) {
        throw std::overflow_error("Divide by zero exception");
}
Duck Dodgers
  • 3,409
  • 8
  • 29
  • 43
0

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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85