Don’t use C++ exception specifications

Exception specifications should be very familiar to Java programmers. Here is an example:

class MyException extends Exception {
}

class Foo {
    void abc(int x) throws MyException {
        if (x < 0) throw new MyException();
    }

    void def() throws MyException {
        abc(3);
    }
}

In the code above, Foo.abc(int) may throw MyException, in the Java language, it must be declared to be thrown (except classes inheriting from Error or RuntimeException). Foo.def() must also be declared to throw MyException because it calls Foo.abc() that may throw MyException and does not catch it (although Foo.def() actually never throws exception). Any attempt to throw exceptions (except classes inheriting from Error or RuntimeException) or ignore exceptions thrown from method calls that are not declared in the signature is a compile-time error. This also applies to extending classes or implementing interfaces. An overriding method or implementing method can declare only stricter exception specifications i.e. a subset of the overridden method or implemented method but not any exceptions not found in the overridden method or implemented method. The whole mechanism is called checked exception and is a part of the type system in Java.

Now comes to C++. C++ has also exception specification and can be applied on any function declaration, pointer to function declaration, reference of function declaration and pointer-to-member function declaration but not in a typedef declaration. Here is the syntax:

void func() throw(int);

The above declaration says that func() may throw only objects of int. If the specification is omitted, unlike in Java, a function may throw anything. To specify that a function may not throw anything, an empty exception specification is needed. The restriction on deriving classes is the same as in Java. An overriding member function can only throw a subset of the overridden function. Also, there are restrictions on assigning pointers to functions. A pointer to function can be assigned to a pointer object to function only if the specification in source value is more restrictive than the specification in destination object.

However, there is a fatal design flaw in the whole system: exceptions are checked at run-time instead of at compile-time. For example, the following code can be compiled:

void oops() throw(int) {
    throw 3.0;
}

int main() {
    try {
        oops();
    } catch (double &e) {
    }
}

oops() is declared to throw objects of int only but an object of double is thrown. In this case, the double object cannot be caught, instead, std::unexpected() is called. The default action is to call std::terminate() which in term abort the program. Although you can supply a custom version of std::unexpected(), it is not allowed to return normally. It can instead throw an allowed exception or terminate the program but the use is not practical because there can be only one handler which is very difficult to handle all unexpected exceptions. Furthermore, the use of exception specifications may create a performance penalty because the compiler must emit code to call std::unexcepted() in case of exception. The compiler may simply refuse to inline some trivial functions if an exception specification is used.

C++ exception specifications, unlike Java, is flawed by design. NEVER use it.

P.S. C++ exception specifications have been deprecated in the next edition of standard, C++0x.

1 Comment


  1. What kind of programming do you do? Perhaps we could work on something. Please send me an email!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *