Fun with exceptions


Here’s a guideline for you: If your library uses exceptions, and you intend users of your library to catch them, don’t implement them inline in the header.

Example:

class LIB_EXPORT MyException : public std::exception {
public:
    explicit MyException( ... );
    // ... (but no dtor)
};

Here, the compiler will generate a MyException destructor for you, since you didn’t provide one. Compiler-synthesised destructors are public, inline, have an empty body, and the same exception specification as the base class’ destructor. So, the above is equivalent to:

class LIB_EXPORT MyException : public std::exception {
public:
    explicit MyException( ... );
    ~MyException() throw() {}
    // ...
};

Unfortunately (well, actually fortunately, but not for our scenario), the destructor of std::exception is virtual, so ~MyException() throw() {} (explicitly written by you or synthesised by the compiler) is the definition (as opposed to declaration) of the first virtual function of the class MyException. Most C++ compilers take that as a cue to emit the MyException virtual function table, as well as its RTTI information at that point.

Oops. Does that mean we’ll end up with duplicate vtables and RTTI information for a class whose first virtual function is defined inline?

Yes, it does. That’s not normally a big problem, since the linker will merge them at link time. However, at least my GCC 4.3 doesn’t do this across shared library boundaries.

Thus, we end up with a situation where the throw site uses a different vtable (and std::type_info instance) for the exception class than the user of the library (which has its own copy helpfully provided by the compiler). The result is that you can’t catch the exception in client code anymore:

// in the client
try {
    doSomethingThatThrowsMyException();
} catch ( const MyException & e ) { // never hit
    log( "Caught MyException: %s", e.what() );
    return;
} catch ( const std::exception & e ) { // hit instead
    log( "Caught unexpected exception %s: %s:", typeid(e).name(), e.what() );
    return;
}

Now, consider the surprise—the sheer terror—of a developer using your library, only to find he’s thrown a MyException that he can’t catch.

Don’t go there. Define your exception classes out-of-line!

[Update 2010-12-02: The problem only occurs if you throw the exception from out-of-line code. If you only throw the exception from inline code, everything is peachy.]

About marcmutz
Marc Mutz is a Principal Software Engineer with The Qt Company.

3 Responses to Fun with exceptions

  1. Jan Kundrát says:

    So that’s why I was bitten by exactly that when I tried to reliably catch exiv2 exceptions from KPhotoAlbum. I’ve checked their (exiv2’s) SVN and now it seems correct, even though the commit message was “merging changes from devel back to trunk” and I’m not really in the mood to dig in extinct SVN branches today — let’s hope they don’t revert back to inline stuff.

    Thanks for a detailed explanation; it is perfectly clear now (in fact, it was clear when I read past the ” definition of the first virtual function” bit).

    Too bad some of your posts are password protected, it seems that you write about an interesting topic. Anyway, have fun!

  2. Aurélien says:

    Great post! I had a very similar experience to Jan with exiv2 exceptions in Gwenview. It makes more sense now.

  3. Interesting! I learned two valuable things from your post: a) an automatically defined dtor uses the same exception-specification as it’s base class’ dtor, b) compilers actually *do* omit vtables when they see it fit.
    This might keep me from a few struggles in similar situations in the future.
    Thanks!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.