Catching Static Exceptions

For objects at the namespace/global scope, exceptions thrown from their constructors can cause a call to terminate() because there is no catch handler around them.


    class X {
    public:
        X( int ) throw ( some_type );
    };
    
    X x( 1 ); // bad if this throws exception

This can be worked around by adding a function try block to the most-derived constructor of the type. Let's assume we can't modify the original type's constructor in this way. A solution is to define a new type that has this function try block.


    class X {
    public:
        X( int ) throw ( some_type );
    };
    
    void static_init_failed() {
        try {
            throw;
        }
        catch ( some_type ) {
            // ...
        }
        catch ( ... ) {
            // ...
        }
    }
    
    class X_wrapper {
    public:
        X x;
        X_wrapper() try : x( 1 ) {
        }
        catch ( ... ) {
            static_init_failed();
        }
    };
    
    X_wrapper x_;
    X& x( x_.x ); // this gives us the exact same access name and type as before

The wrapper can be templatized for reusability.

Another approach is to set up a terminate handler prior to construction of the static objects whose construction that may cause an exception.


    void static_init_failed() {
        // do cleanup/reporting
        std::exit( EXIT_FAILURE );
    }
    
    class setup_handler {
    public:
        setup_handler() {
            std::set_terminate( static_init_failed );
        }
    };
    
    setup_handler setup;
    
    class X {
    public:
        X( int ) throw ( some_type );
    };
    
    X x( 1 );

This approach has many disadvantages.

Setting up the handler must be arranged prior to cases where a static exception may occur. This can be done by either defining the setup_handler object before any relevant static objects in a given source file are defined, or using some implementation-specific method of installing the handler before any other static initialization takes place.

Being a terminate handler, it may be invoked for other reasons than an exception leaking from a static object. One case is in conjunction with an exception specification violation and the unexpected() handler. Another is when an exception is thrown during exception handling. A more serious case is when the exception mechanism is corrupt. In this case, it would not be a good idea to use anything that could cause exceptions to be internally thrown and caught, as this may cause bad things if the exception mechanism is already corrupt.

The Big Picture

I've found that it's best to avoid this situation completely by not defining namespace-scope non-trivial objects that may throw exceptions during construction. They can be put inside some structure that is created when the module is actually used, which allows users of the module itself to catch and handle the exceptions in a much better environment.


Blargg's C++ Notes