Catching an exception

As mentioned earlier, one of the advantages of C++ exception handling is that it allows you to concentrate on the problem you?re actually trying to solve in one place, and then deal with the errors from that code in another place.

The try block

If you?re inside a function and you throw an exception (or a called function throws an exception), the function exits in the process of throwing. If you don?t want a throw to leave a function, you can set up a special block within the function where you try to solve your actual programming problem (and potentially generate exceptions). This block is called the try block because you try your various function calls there. The try block is an ordinary scope, preceded by the keyword try:

try {
// Code that may generate exceptions

If you check for errors by carefully examining the return codes from the functions you use, you need to surround every function call with setup and test code, even if you call the same function several times. With exception handling, you put everything in a try block and handle exceptions after the try block. Thus, your code is a lot easier to write and easier to read because the goal of the code is not confused with the error checking.

Exception handlers

Of course, the thrown exception must end up some place. This place is the exception handler, and you need one exception handler for every exception type you want to catch. Exception handlers immediately follow the try block and are denoted by the keyword catch:

try {
// Code that may generate exceptions
} catch(type1 id1) {
// Handle exceptions of type1
} catch(type2 id2) {
// Handle exceptions of type2
} catch(type3 id3)
// Etc...
} catch(typeN idN)
// Handle exceptions of typeN
// Normal execution resumes here...

The syntax of a catch clause resembles functions that take a single argument. The identifier (id1, id2, and so on) can be used inside the handler, just like a function argument, although you can omit the identifier if it?s not needed in the handler. The exception type usually gives you enough information to deal with it.

The handlers must appear directly after the try block. If an exception is thrown, the exception-handling mechanism goes hunting for the first handler with an argument that matches the type of the exception. It then enters that catch clause, and the exception is considered handled. (The search for handlers stops once the catch clause is found.) Only the matching catch clause executes; control then resumes after the last handler associated with that try block.
Notice that, within the try block, a number of different function calls might generate the same type of exception, but you need only one handler.
To illustrate using try and catch, the following variation of Nonlocal.cpp replaces the call to setjmp( ) with a try block and replaces the call to longjmp( ) with a throw statement.

// Illustrates exceptions
#include <iostream>
using namespace std;
class Rainbow {
Rainbow() { cout << "Rainbow()" << endl; }
~Rainbow() { cout << "~Rainbow()" << endl; }
void oz() {
Rainbow rb;
for(int i = 0; i < 3; i++)
cout << "there's no place like home\n";
throw 47;
int main() {
try {
cout << "tornado, witch, munchkins...\n";
catch (int) {
cout << "Auntie Em! "
<< "I had the strangest dream..."
<< endl;
} ///:~

When the throw statement in oz( ) executes, program control backtracks until it finds the catch clause that takes an int parameter, at which point execution resumes with the body of that catch clause. The most important difference between this program and Nonlocal.cpp is that the destructor for the object rb is called when the throw statement causes execution to leave the function oz( ).

There are two basic models in exception-handling theory: termination and resumption. In termination (which is what C++ supports), you assume the error is so critical that there?s no way to automatically resume execution at the point where the exception occurred. In other words, ?whoever? threw the exception decided there was no way to salvage the situation, and they don?t want to come back.

The alternative error-handling model is called resumption, first introduced with the PL/I language in the 1960s . Using resumption semantics means that the exception handler is expected to do something to rectify the situation, and then the faulting code is automatically retried, presuming success the second time. If you want resumption in C++, you must explicitly transfer execution back to the code where the error occurred, usually by repeating the function call that sent you there in the first place. It is not unusual, therefore, to place your try block inside a while loop that keeps reentering the try block until the result is satisfactory.

Historically, programmers using operating systems that supported resumptive exception handling eventually ended up using termination-like code and skipping resumption. Although resumption sounds attractive at first, it seems it isn?t quite so useful in practice. One reason may be the distance that can occur between the exception and its handler; it is one thing to terminate to a handler that?s far away, but to jump to that handler and then back again may be too conceptually difficult for large systems on which the exception can be generated from many points.