Exceptions in C++

This page contains my personal observations about exception handling in some C++ compilers. If you have things to add or correct, let me know! Thanx to Jobst Köhne for helpful discussions on the subject.

IRIX 5.3 and 6.2

Experiences under IRIX 5.3 with GNU g++, version 2.7.2.1

Programs compile and run, but no exception handling done

GNU g++ has a compiler option -fhandle-exceptions that seems to suggest that it can handle exceptions. Alas, the option only ensures that the exception-handling related constructs (with the keywords try, throw, and catch) are running through the compiler correctly. However, the "throw" statements are ignored, and consequently "catch" statements are never executed.

Nevertheless, the error variables are instantiated and destroyed upon throw, as the output of the program c++test.C shows.

Experiences under IRIX 5.3 with CC

No exception handling at all

Programs with exception-handling related constructs will not compile. The compiler error reads
error(3639): support for exception handling is disabled

Solaris 4x.55

Experiences under Solaris with GNU g++, version 2.7.2.1

Good news: it works

Under Solaris, g++ can handle exceptions, when the option -fhandle-exceptions is used: Look at the output of the program c++test.C.

Bad news: not perfectly: Derived error classes are not handled

There are still some imperfections, though. An exception class (derivederror in the example) derived from a superclass (error) should be caught when the superclass is mentioned in the catch statement. This is to avoid the necessity that the user has to give a comprehensive list of all possible error classes in the catch statement. By using virtual functions (like print in the example) it is still possible to get the more comprehensive information from the derived class.

However, derived exceptions are not correctly caught under the name of their superclass, but only when there is an exact match.

Stack unwinding

An important feature of exception handling is stack unwinding. When a function is left because of an uncaught exception, the destructors of all local objects must be called. This is handled correctly, as the test program output shows (destructor for i4 is always called).

Stack unwinding for global variables

However, for global variables this is not the case: If the main program is terminated because of an uncaught exception, destructors of global variables are not called (try it!). To avoid this (which should be done, especially if global variable destructors are used to disconnect files etc.), the main program should look something like this:
int main() {
  try{
//  here comes the "real" main program
//  ...
    return 0; // All is well that ends well.
  }
  catch (...) {
//  something went wrong. Do nothing, but don't leave exception uncaught.
    return 1; // It didn't end well...
  }
}

Strange: Effect of destructors

A somewhat unexpected feature of exception handling can also seen from the output of the test program:

Apparently the destructor of the thrown object is called before the catch statement (namely during the stack unwinding). No constructor is called for the caught object, nor a destructor. Nevertheless the caught object contains the state of the thrown object before the call to the destructor.

I have no idea how this might affect the program if the thrown object contains pointers. Virtual functions are not affected, because derived objects can only be caught by exact match anyway.

Experiences under Solaris with CC

Good news: it works

Under Solaris, CC can handle exceptions. No special options have to be given. Look at the output of the program c++test.C.

Very god news: Even derived error classes are handled

The derived error class in the example is caught correctly, and the correct virtual debug print function is called.

The compiler also warns about the (for a correctly woking compiler) superfluous catch statement for the derived exception after the base class exception:

[amrum] ~/www/c++ $ CC c++test.C -o c++test.CC.solaris4x_55             
"c++test.C", line 80: Warning: Handler for a derived class placed after
handler for its base class.
1 Warning(s) detected.

Stack unwinding for global variables

Here CC seems to have a problem. Without the catch statement, not evem the cout buffers are flushed, so that no output appears. But the user gets a meaningful error message from the shell:

[amrum] ~/www/c++ $ c++test.CC.solaris4x_55
Run-time exception error; current exception: derivederror
        No handler for exception.
zsh: 9726 IOT instruction  c++test.CC.solaris4x_55

Call of destructors

For CC, also the call of destructors for thrown objects happens at the point where it is expected: Namely when the caught object goes out of scope.

Fazit

The only compiler with a decent exception handling tested here is CC on Solaris. But at least such compilers do exist!