Resumable Exception Framework

Often it is argued that C++ should include resumable exceptions. While this might be a benefit to the language, I haven't seen examples of sound design using them. Without practical data, I can only guess about their usefulness. In the meantime, those that want them can use the features provided by the current language definition to get what they want in a fairly convenient manner.

The code to implement them is simplified to make presentation clearer. There are dimensions missing that would likely be present in a library form of this, to enable reuse.

The basic idea of a resumable exception is that some server encounters a condition that is exceptional. At this point, one of two things can happen: if the user has provided a handler for this condition, the user's handler will be invoked; otherwise, a normal terminating exception will be thrown

This behavior can be packaged so that the server and client don't have to be exposed to the details of implementation. When the server encounters the situation, it asks a handler object to handle the condition. This handler will either return, in the case of a retry, or terminate with an exception. The client can register their handler object with the server and also set up a catch handler for the terminating exception.

On to the code...


    // exception class - thrown for terminating condition
    struct Unhandled { };

    // a handler for the condition
    class Handler {
        Handler* const prev;
    public:
        Handler();
        virtual void handle() {
            throw Unhandled();
        }
    };

    // currently-registered handler
    Handler* handler;

    // normal handler that throws exception
    Handler default_handler; // will set handler

    // manage stack of handlers
    Handler::Handler() : prev( handler ) { handler = this; }
    Handler::~Handler() { handler = prev; }


    void server() {
        // ...
        while ( true )
        {
            // ...
            if ( !some_condition )
                break;
            
            handler->handle();
            // if handler returns, operation is retried by looping back
        }
    }

    void user_that_provides_handler()
    {
        struct : Handler
        {
            int shared_member;
            
            void handle() {
                // ...
                if ( cant_handle )
                    Handler::handle(); // throws exception
            }
        } handler;

        handler.shared_member = 1234;

        server();
    }
    
    void user_that_catches_exception()
    {
        try {
            Handler handler; // use default handler that throws exception
            server();
        }
        catch ( Unhandled ) {
            // lib encountered some_condition
        }
    }

In user_that_provides_handler(), the user provides the handler using a local unnamed type with a virtual function. This local type can also have member variables, which allows the effect of sharing data between the handler and the function.

In user_that_catches_exception(), the user just catches the (terminating) exception when the condition arises.

Another possibility is to have Handler::handle() delegate the call to the previous handler (if there is one). This would allow nested handlers to pass responsibility up to the enclosing handler.


Blargg's C++ Notes