384

I have a very poor understanding of exception handling(i.e., how to customize throw, try, catch statements for my own purposes).

For example, I have defined a function as follows: int compare(int a, int b){...}

I'd like the function to throw an exception with some message when either a or b is negative.

How should I approach this in the definition of the function?

Terry Li
  • 16,870
  • 30
  • 89
  • 134
  • 9
    Superfluous exceptions are worth avoiding. If you don't want your caller to pass negative values make it more obvious by specifying `unsigned int` as the parameters in your function signature. Then again I'm of the school that you should only throw and catch exceptions for things that are actually exceptional. – AJG85 Dec 12 '11 at 21:00
  • 2
    @Mark: I originally misunderstood the question to be about whether one should use `throw()` exception specifications on functions. – Oliver Charlesworth Dec 12 '11 at 21:00
  • More generically, there's [this](https://www.w3schools.com/cpp/cpp_exceptions.asp), which shows that you can `throw 5;` or `throw "Bad!";` and `catch (...) { }` etc... – Andrew Oct 18 '20 at 06:10

5 Answers5

512

Simple:

#include <stdexcept>

int compare( int a, int b ) {
    if ( a < 0 || b < 0 ) {
        throw std::invalid_argument( "received negative value" );
    }
}

The Standard Library comes with a nice collection of built-in exception objects you can throw. Keep in mind that you should always throw by value and catch by reference:

try {
    compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
    // do stuff with exception... 
}

You can have multiple catch() statements after each try, so you can handle different exception types separately if you want.

You can also re-throw exceptions:

catch( const std::invalid_argument& e ) {
    // do something

    // let someone higher up the call stack handle it if they want
    throw;
}

And to catch exceptions regardless of type:

catch( ... ) { };
Alexander Malakhov
  • 3,383
  • 2
  • 33
  • 58
nsanders
  • 12,250
  • 2
  • 40
  • 47
  • You single quoted the message – Adrian Cornish Dec 12 '11 at 20:55
  • 39
    And you should always catch exceptions as const – Adrian Cornish Dec 12 '11 at 20:55
  • I prefer a user-defined exception to invalid_argument. In my case, it is not necessarily an invalid argument. – Terry Li Dec 12 '11 at 20:56
  • @AdrianCornish good catch on single quotes. Const is less important but still reasonable to use. – nsanders Dec 12 '11 at 21:01
  • 2
    @TerryLiYifeng if custom exceptions make more sense then go for it. You may still want to derive from std::exception and keep the interface the same. – nsanders Dec 12 '11 at 21:02
  • 2
    +1'ed again but I think const its pretty important - because it highlights the fact it is a temporary object now - so modification is useless. – Adrian Cornish Dec 12 '11 at 21:03
  • 3
    @AdrianCornish: It's not really temporary though. Non-const catches [can be useful](http://www.boost.org/doc/libs/1_48_0/libs/exception/doc/tutorial_transporting_data.html). – GManNickG Dec 12 '11 at 21:29
  • @GMan - Agree it can be useful - I think things should be const by default then if you find you need to modify change. This creates a temporary though std::exception e; throw e; – Adrian Cornish Dec 12 '11 at 22:03
  • @AdrianCornish: Yup, but that stands in contrast to saying modification is useless on a temporary. – GManNickG Dec 12 '11 at 22:05
  • 33
    You'd usually rethrow with a simple `throw;` (rethrowing the original object and preserving its type) rather than `throw e;` (throwing a copy of the caught object, possibly changing its type). – Mike Seymour Dec 12 '11 at 22:55
  • @TerryLi - Note that, in case that your custom exception derives from `std::exception`, than, upon catching the exceptions, you should pay attention to the order in which you catch "all possible" exceptions that are thrown (see my answer for more details). – Guy Avraham Sep 19 '18 at 09:13
  • None of the exception objects defined in the standard library `std` have a very good copy constructor. As such, if you try to construct a copy an exception and raise the new exception, strange things happen. For example, a string message such as `"integer passed into operator[] is too large"` will be lost/destroyed if you call the copy constructor. `new_exception = ExceptionClass(old_exception)`. In general, the exceptions defined in the `std` library are bad. – Toothpick Anemone Aug 11 '22 at 17:34
27

Though this question is rather old and has already been answered, I just want to add a note on how to do proper exception handling in C++11:

Use std::nested_exception and std::throw_with_nested

It is described on StackOverflow here and here, how you can get a backtrace on your exceptions inside your code without need for a debugger or cumbersome logging, by simply writing a proper exception handler which will rethrow nested exceptions.

Since you can do this with any derived exception class, you can add a lot of information to such a backtrace! You may also take a look at my MWE on GitHub, where a backtrace would look something like this:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
GPMueller
  • 2,881
  • 2
  • 27
  • 36
21

Just add throw where needed, and try block to the caller that handles the error. By convention you should only throw things that derive from std::exception, so include <stdexcept> first.

int compare(int a, int b) {
    if (a < 0 || b < 0) {
        throw std::invalid_argument("a or b negative");
    }
}

void foo() {
    try {
        compare(-1, 0);
    } catch (const std::invalid_argument& e) {
        // ...
    }
}

Also, look into Boost.Exception.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
12

You could define a message to throw when a certain error occurs:

throw std::invalid_argument( "received negative value" );

or you could define it like this:

std::runtime_error greatScott("Great Scott!");          
double getEnergySync(int year) {                        
    if (year == 1955 || year == 1885) throw greatScott; 
    return 1.21e9;                                      
}                                                       

Typically, you would have a try ... catch block like this:

try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
serup
  • 3,676
  • 2
  • 30
  • 34
12

Adding to this answer, as it doesn't seem advantageous to create another answer for this Q&A at this time.

In the case where you create your own custom exception, that derives from std::exception, when you catch "all possible" exceptions types, you should always start the catch clauses with the "most derived" exception type that may be caught. See the example (of what NOT to do):

#include <iostream>
#include <string>

using namespace std;

class MyException : public exception
{
public:
    MyException(const string& msg) : m_msg(msg)
    {
        cout << "MyException::MyException - set m_msg to:" << m_msg << endl;
    }

   ~MyException()
   {
        cout << "MyException::~MyException" << endl;
   }

   virtual const char* what() const throw () 
   {
        cout << "MyException::what" << endl;
        return m_msg.c_str();
   }

   const string m_msg;
};

void throwDerivedException()
{
    cout << "throwDerivedException - thrown a derived exception" << endl;
    string execptionMessage("MyException thrown");
    throw (MyException(execptionMessage));
}

void illustrateDerivedExceptionCatch()
{
    cout << "illustrateDerivedExceptionsCatch - start" << endl;
    try 
    {
        throwDerivedException();
    }
    catch (const exception& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << endl;
        // some additional code due to the fact that std::exception was thrown...
    }
    catch(const MyException& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an MyException, e.what::" << e.what() << endl;
        // some additional code due to the fact that MyException was thrown...
    }

    cout << "illustrateDerivedExceptionsCatch - end" << endl;
}

int main(int argc, char** argv)
{
    cout << "main - start" << endl;
    illustrateDerivedExceptionCatch();
    cout << "main - end" << endl;
    return 0;
}

NOTE:

  1. The proper order should be vice-versa, i.e.- first you catch (const MyException& e) which is followed by catch (const std::exception& e).

  2. As you can see, when you run the program as is, the first catch clause will be executed (which is probably what you did NOT want in the first place).

  3. Even though the type caught in the first catch clause is of type std::exception, the "proper" version of what() will be called - cause it is caught by reference (change at least the caught argument std::exception type to be by value - and you will experience the "object slicing" phenomena in action).

  4. In case that the "some code due to the fact that XXX exception was thrown..." does important stuff WITH RESPECT to the exception type, there is misbehavior of your code here.

  5. This is also relevant if the caught objects were "normal" object like: class Base{}; and class Derived : public Base {}...

  6. g++ 7.3.0 on Ubuntu 18.04.1 produces a warning that indicates the mentioned issue:

In function ‘void illustrateDerivedExceptionCatch()’: item12Linux.cpp:48:2: warning: exception of type ‘MyException’ will be caught catch(const MyException& e) ^~~~~

item12Linux.cpp:43:2: warning: by earlier handler for ‘std::exception’ catch (const exception& e) ^~~~~

Again, I will say, that this answer is only to ADD to the other answers described here (I thought this point is worth mentioning, yet could not depict it within a comment).

JΛYDΞV
  • 8,532
  • 3
  • 51
  • 77
Guy Avraham
  • 3,482
  • 3
  • 38
  • 50
  • actually you should first catch specific exceptions and then the more general one. In your case first catch catch(const MyException& e) and then catch (const exception& e). since excecption is the base class, you would never go into the 2. scope in your case – Chief Nov 11 '22 at 14:16
  • 1
    @Chief - You are 100% right. This is why I wrote "of what you should NOT do" :) – Guy Avraham Nov 12 '22 at 19:08
  • oh im sorry i must have missed that – Chief Nov 14 '22 at 11:51
  • Can someone explain what `virtual const char* what() const throw () {...}` declaration is? Is this a method that is named `what` or is it named `throw`? Why are there two parentheses pair in the function definition? – MTV Nov 21 '22 at 21:09