cexcept: rationale 2.0.0 (2001-Jul-12-Thu)
Adam M. Costello <amc@cs.berkeley.edu>

Rationale for some of the design decisions behind version 2.0.* of the
cexcept.h interface.  See README for copyright information.


Questions answered below:
  * Why aren't multiple Catch clauses allowed?
  * Why can't I say Catch (int e) like in C++ or Java?
  * Why can't I jump into or out of a Try clause?
  * Why isn't a Finally clause supported?
  * Why can't I use Throw without an expression to re-throw an exception?
  * Why can't "real" exceptions be caught?
  * Why must the Catch expression have the same type that was passed to
    define_exception_type()?  I can assign a double value to an int, so
    why can't I catch a double exception in an int variable?
  * Why does the implementation copy the exception twice?
  * Why are the macros spelled Try, Catch, Throw rather than...?


Why aren't multiple Catch clauses allowed?

    The purpose of multiple catch clauses in C++ or Java is to allow you
    to catch multiple exception types.  This is natural in a language
    that supports polymorphism, where you can substitute objects of
    different types into the same expression and cause different code to
    be executed.  In such a language you can throw any type of object,
    and the appropriate catch clause will be selected automatically.

    C does not support polymorphism (except for unions and void
    pointers).  Therefore it is natural in C to have just one exception
    type, and therefore one Catch clause is sufficient.  The application
    programmer is free to use any type for the exception type, and may
    use a union to simulate polymorphism (see example2.c for an example
    of this).  But for many applications an int, enum, or small struct
    will be sufficient.


But couldn't tricks be played to allow multiple exception types?

    Yes, but cexcept.h is intended for C programs and C programmers.  If
    the concept of polymorphism were incorporated into the interface,
    the interface would become less intuitive to many of the people
    it is designed for.  But if an application programmer uses unions
    to simulate multiple exception types, the resulting code will be
    perfectly understandable to other C programmers, even if they have
    no experience with polymorphism.


Why can't I say Catch (int e) like in C++ or Java?

    Again, the interface tries to be intuitive to C programmers.  In C,
    declarations go at the beginnings of blocks.  The cexcept syntax is
    actually more flexible in a way, because any lvalue can go in the
    parentheses, for example: Catch (p->e)


Why can't I jump into or out of a Try clause?

    There is some hidden setup and cleanup that needs to be done
    regardless of whether an exception is caught.  Jumping into a Try
    clause would avoid doing the setup, and jumping out of it would
    avoid doing the cleanup.


Why isn't a Finally clause supported?

    There's no way to implement it.  In Java, the main difference
    between putting code in a finally clause versus putting the code
    after the try/catch statement is that the finally clause will get
    executed even if a return or throw occurs within the try clause or
    catch clause.  But in C, a return statement returns immediately.

    In Java one could make do without a finally clause by deferring
    any return from the try clause (by storing the return value in a
    variable), and moving some or all of the catch code outside the
    try/catch statement, like so:

        Exception e = null;

        try { ... }
        catch (Exception tmp) { e = tmp; }

        // Do cleanup here.

        if (e instanceof Foo) { ... }
        else if (e instanceof Bar) { ... }
        ...
        else if (e != null) throw e;

    A similar technique can be used with cexcept:

        my_exception_type e = some null value that will not be thrown;

        Try { ... }
        Catch (e) { }

        /* Do cleanup here. */

        if (e is not that null value) { ... }


Why can't I use Throw without an expression to re-throw an exception?

    The use of throw without an expression is needed in C++ because it's
    the only way to re-throw an exception of unknown type:

        catch (...) {
          // do some stuff
          throw;
        }

    This syntax is not provided in Java because the fact that all
    exceptions are instances of Exception eliminates the problem:

        catch (Exception e) {
          // do some stuff
          throw e;
        }

    Similarly, with cexcept, the type of exceptions is always known, so
    an expressionless Throw is not needed.  Besides, the syntax would be
    very difficult, if not impossible, to implement.


Why can't "real" exceptions be caught?

    "Real" exceptions (like floating-point exceptions and
    addressing exceptions) are platform-dependent, but cexcept is a
    platform-independent abstraction of setjmp/longjmp.  The only way
    to jump to a setjmp() is from a longjmp(), so a Catch requires an
    explicit Throw.

    On POSIX systems, it is possible to call setjmp() from within a
    signal handler, and hence possible to use Throw within a signal
    handler, but this is very likely to cause subtle bugs and is not
    recommended.  If a program uses both signals and cexcept, it may
    wish to modify cexcept.h to use the POSIX functions sigsetjmp() and
    siglongjmp() instead of the standard C library functions setjmp()
    and longjmp().


Why must the Catch expression have the same type that was passed to
define_exception_type()?  I can assign a double value to an int, so why
can't I catch a double exception in an int variable?

    The intuitive expectation is that "Throw v" and "Catch (e)" should
    have an effect equivalent to "e = v".  The latter converts v to the
    type of e, but the Throw statement has no way of knowing the type of
    e, so it converts v to the type passed to define_exception_type().
    Depending on how cexcept is implemented, unintuitive or even
    undefined behavior could result if e has a different type.


Why does the implementation copy the exception twice?

    The Throw statement must store the exception value somewhere.  If it
    stores directly into the object named by the Catch expression, then
    that object must not be a non-volatile automatic object.  It was
    deemed preferable to copy the exception twice rather than prohibit
    application programmers from catching exceptions in non-volatile
    automatic objects (which is what they would most often be inclined
    to use).


Why are the macros spelled Try, Catch, Throw rather than...?

    We considered many other possibilities:

        try catch throw
        TRY CATCH THROW
        ctry ccatch cthrow
        c_try c_catch c_throw
        cex_try cex_catch cex_throw
        CEX_TRY CEX_CATCH CEX_THROW
        cexcept_try cexcept_catch cexcept_throw
        try_until_exception catch_exception throw_exception

    We wanted names that would be easy to read, easy to type, intuitive,
    and unlikely to conflict with existing names.  Ultimately we settled
    on Try, Catch, and Throw as the best compromise among all these
    criteria.
