Exception

Resumption

Resumption is an exception-raising mechanism where the handler runs in place of the faulting code, then control returns to the raiser — no stack unwinding happens.

Why have resumption when we already have termination (throw/catch)?

Termination forcibly unwinds the stack to the handler — the raiser’s execution is gone. That’s the right model when the fault means you can’t continue. But some faults are repairable — the handler can fix the situation and the raiser should resume where it left off. Termination can’t do that; resumption can. Think of it as “asking for help” rather than “giving up”.

uC++ raising syntax

_Throw  [ exception-type ] ;                     // termination — unwinds
_Resume [ exception-type ] [ _At uBaseCoroutine-id ] ;  // resumption — no unwind
  • _Throw with no exception-type is a rethrow.
  • _Resume with no exception-type is a re-resume.
  • _At targets another coroutine — see Nonlocal Exception.

Handler syntax

uC++ extends try blocks with _CatchResume clauses alongside normal catch:

try {
    ...
} _CatchResume( E1 ) { ... }       // must appear before catch clauses
  _CatchResume( ... ) { ... }       // catch-any for resumption — must be last _CatchResume
  catch( E2 ) { ... }               // catch clauses after all _CatchResume
  catch( ... ) { ... }              // catch-any for termination

Tip

All _CatchResume handlers must precede any catch handlers. A resumption handler cannot perform break, continue, goto, or return — control must return to the raise site.

Resumption matches only _Resume; termination matches only _Throw

The raise mechanism dictates which handler set is searched:

  • _Throw → only catch clauses are examined
  • _Resume → only _CatchResume clauses are examined

They don’t cross over. A _Throw will never be caught by _CatchResume, and vice versa.

Worked example

_Exception E {};
int main() {
    try {
        cout << "before throw" << endl;
        _Throw E{};
        cout << "after throw" << endl;  // never reached — stack unwinds
    } catch( E ) {
        cout << "catch" << endl;
    }
    cout << "after try1" << endl;
 
    try {
        cout << "before resume" << endl;
        _Resume E{};
        cout << "after resume" << endl;  // IS reached — handler returned
    } _CatchResume( E ) {
        cout << "_CatchResume" << endl;
    }
    cout << "after try2" << endl;
}

Output:

before throw
catch
after try1
before resume
_CatchResume
after resume
after try2

Note the key difference: after throw is skipped (termination unwinds past it), but after resume runs (the _CatchResume handler ran, then control returned).

defaultResume

Every resumption-raising exception has a defaultResume — what happens if no _CatchResume handler matches during propagation. uC++‘s default is to convert the resumption into a termination (_Throw the same exception), so an unhandled _Resume degrades gracefully to a catch search.