Levent Kaya
Levent Kaya

Reputation: 73

Exception handling with stoi function

I have a little problem with exception handling in c++. I need to convert a number to string and if this string contains letters or more than 10 characters I need to give error. Here is the exception-handling part;

 while ( cin >> input )
   {
      try
      { 
         convert = castInput( input );
         cout << "The number entered was: " << convert;
      } 
      catch ( NonNumber &excp )
      {
         cout << "INVALID INPUT: " << excp.what();
      } 
      catch ( Overflow &excp )
      {
         cout << "INVALID INPUT: " << excp.what();
      } 

      cout << "\n\nPlease enter a number (end-of-file to terminate): ";
   }

I used stoi function to converting the string to int but I think I need open 2 classes. I don't know why and how becase stoi fucntion own what function already.

Upvotes: 5

Views: 23252

Answers (2)

A M
A M

Reputation: 15267

I personally find that a regex is no overkill for this. Checking user input with a regex will have no negative impact on performance whatsoever.

But I think that you are more interested in exception handling. You need to read many many pages of a good C++ book to understand exception handling and its use cases.

And maybe, but I do not know, you want simply to catch the standard exceptions thrown by std::stoi. These are:

That would be easy with writing

        // Standard exceptions for stoi
        catch (const std::invalid_argument & e) {
            std::cout << e.what() << "\n";
        }
        catch (const std::out_of_range & e) {
            std::cout << e.what() << "\n";
        }

But maybe you want to learn, how to write own exceptions. For your specific requirements. But please note that your specification, e.g. "more than 10 digits" and "no letter" are maybe not quite what you want. With a machine, where int is 32bit bit, the maximum number that can be converted is: 2147483647. Any number bigger and still having only 10 characters, will throw a std::out_of_range. On the other hand numbers like 123X would be converted by std::stoi to 123. So, maybe your requirements are not so clear.

Anyway, to show you, how you could use own exceptions, I create 2 customized exceptions classes. And to make life easy, I derived those from std::exception (recommended).

See the below example:

#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>

class StoiException : public std::exception
{
public:
    StoiException(const std::string& msg) : message(msg) {}
    virtual const char* what() const noexcept override { return message.c_str(); }
protected:
    void setMessage(const std::string& msg) { message = msg; }
protected:
    std::string message{};
};

class NoNumber : StoiException
{
public:
    NoNumber(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> NoNumber: ") + msg); }
    virtual const char* what() const noexcept override { return message.c_str(); }
};
class Overflow : StoiException
{
public:
    Overflow(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> Overflow: ") + msg); }
    virtual const char* what() const noexcept override { return message.c_str(); }
};

int castInput(std::string& input) {
    int result{};
    // Check, if there is any letter in the input string
    if (std::any_of(input.begin(), input.end(), isalpha)) {
        // If so, than throw
        throw NoNumber(input);
    }
    // Check, if string has more than 10 characters
    if (input.length() > 10) {
        // If so, than throw
        throw Overflow(input);
    }
    result = std::stoi(input);
    return result;
}

std::istringstream testCin{ R"(123
   567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };


int main() {

    std::string input{};
    // Read all input
    while (testCin >> input) {
        try {
            // Convert
            int convert = castInput(input);
            // This will only be shown , if there is no exception
            std::cout << "\nConverted Number is: " << convert << "\n";
        }
        // Catch all exceptions
        catch (const NoNumber & e) {
            std::cout << e.what() << "\n";
        }
        catch (const Overflow & e) {
            std::cout << e.what() << "\n";
        }
        // Standard exceptions for stoi
        catch (const std::invalid_argument & e) {
            std::cout << e.what() << "\n";
        }
        catch (const std::out_of_range & e) {
            std::cout << e.what() << "\n";
        }
    }
    return 0;
}

Of course you can handle the std::stoi's exception also in your custom converter function. Then only your owwn exceptions are visible.

Please see:

#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>

class StoiException : public std::exception
{
public:
    StoiException(const std::string& msg) : message(msg) {}
    virtual const char* what() const noexcept override { return message.c_str(); }
protected:
    void setMessage(const std::string& msg) { message = msg; }
protected:
    std::string message{};
};

class NoNumber : StoiException
{
public:
    NoNumber(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> NoNumber: ") + msg); }
    virtual const char* what() const noexcept override { return message.c_str(); }
};
class Overflow : StoiException
{
public:
    Overflow(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> Overflow: ") + msg); }
    virtual const char* what() const noexcept override { return message.c_str(); }
};

int castInput(std::string& input) {
    int result{};
    // Check, if there is any letter in the input string
    if (std::any_of(input.begin(), input.end(), isalpha)) {
        // If so, than throw
        throw NoNumber(input);
    }
    // Check, if string has more than 10 characters
    if (input.length() > 10) {
        // If so, than throw
        throw Overflow(input);
    }
    try {
        result = std::stoi(input);
    }
    // Standard exceptions for stoi
    catch (const std::invalid_argument & e) {
        throw NoNumber(input);
    }
    catch (const std::out_of_range & e) {
        throw Overflow(input);
    }
    return result;
}

std::istringstream testCin{ R"(123
   567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };


int main() {

    std::string input{};
    // Read all input
    while (testCin >> input) {
        try {
            // Convert
            int convert = castInput(input);
            // This will only be shown , if there is no exception
            std::cout << "\nConverted Number is: " << convert << "\n";
        }
        // Catch all exceptions
        catch (const NoNumber & e) {
            std::cout << e.what() << "\n";
        }
        catch (const Overflow & e) {
            std::cout << e.what() << "\n";
        }
    }
    return 0;
}

But maybe, what you really want to have, is a function, that encapsulates std::stoi and has an additional return value to show, if it worked or not.

Please note: The last solution will also convert "123X" to 123. That is the difference to the previous versions.

#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>


std::pair<bool, int> castInput(std::string& input) {
    bool ok{ false };
    int result{};
    try {
        result = std::stoi(input);
        ok = true;
    }
    // Catch stoi errors
    catch (const std::invalid_argument & e) {}
    catch (const std::out_of_range & e) {}
    return {ok, result};
}

std::istringstream testCin{ R"(123
   567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };


int main() {
    std::string input{};
    // Read all input
    while (testCin >> input) {
            const auto [ok, convert] = castInput(input);
            if (ok)
                std::cout << "Converted value: " << convert << "\n";
            else
                std::cout << "String '" << input << "' could not be converted\n";
    }
    return 0;
}

Upvotes: 8

snaipeberry
snaipeberry

Reputation: 1059

Why not use Regex ? You just create a regex that check if there is letters or more than 10 chars, depending on the return you process the conversion or throw an custom exception.

This may be the regex you are looking for: ^[0-9]{1,10}$

std::regex reg("^[0-9]{1,10}$");
if (std::regex_match(string, reg))
    // Do what you want (process convert)

Here is the regex cpp reference : here

Edit: As commented, regex is overkill for this, so you could simply make some basic checks inside you castInput function and throw NonNumber if you find a character in the string, or throw Overflow if string.len > 10.

Upvotes: 1

Related Questions