fukasawa e60969
/*===
fukasawa e60969
cexcept.h 2.0.1 (2008-Jul-19-Sat)
fukasawa e60969
http://www.nicemice.net/cexcept/
fukasawa e60969
Adam M. Costello
fukasawa e60969
http://www.nicemice.net/amc/
fukasawa e60969
fukasawa e60969
An interface for exception-handling in ANSI C (C89 and subsequent ISO
fukasawa e60969
standards), developed jointly with Cosmin Truta.
fukasawa e60969
fukasawa e60969
    Copyright (c) 2000-2008 Adam M. Costello and Cosmin Truta.
fukasawa e60969
    This software may be modified only if its author and version
fukasawa e60969
    information is updated accurately, and may be redistributed
fukasawa e60969
    only if accompanied by this unaltered notice.  Subject to those
fukasawa e60969
    restrictions, permission is granted to anyone to do anything
fukasawa e60969
    with this software.  The copyright holders make no guarantees
fukasawa e60969
    regarding this software, and are not responsible for any damage
fukasawa e60969
    resulting from its use.
fukasawa e60969
fukasawa e60969
The cexcept interface is not compatible with and cannot interact
fukasawa e60969
with system exceptions (like division by zero or memory segmentation
fukasawa e60969
violation), compiler-generated exceptions (like C++ exceptions), or
fukasawa e60969
other exception-handling interfaces.
fukasawa e60969
fukasawa e60969
When using this interface across multiple .c files, do not include
fukasawa e60969
this header file directly.  Instead, create a wrapper header file that
fukasawa e60969
includes this header file and then invokes the define_exception_type
fukasawa e60969
macro (see below).  The .c files should then include that header file.
fukasawa e60969
fukasawa e60969
The interface consists of one type, one well-known name, and six macros.
fukasawa e60969
fukasawa e60969
fukasawa e60969
define_exception_type(type_name);
fukasawa e60969
fukasawa e60969
    This macro is used like an external declaration.  It specifies
fukasawa e60969
    the type of object that gets copied from the exception thrower to
fukasawa e60969
    the exception catcher.  The type_name can be any type that can be
fukasawa e60969
    assigned to, that is, a non-constant arithmetic type, struct, union,
fukasawa e60969
    or pointer.  Examples:
fukasawa e60969
fukasawa e60969
        define_exception_type(int);
fukasawa e60969
fukasawa e60969
        enum exception { out_of_memory, bad_arguments, disk_full };
fukasawa e60969
        define_exception_type(enum exception);
fukasawa e60969
fukasawa e60969
        struct exception { int code; const char *msg; };
fukasawa e60969
        define_exception_type(struct exception);
fukasawa e60969
fukasawa e60969
    Because throwing an exception causes the object to be copied (not
fukasawa e60969
    just once, but twice), programmers may wish to consider size when
fukasawa e60969
    choosing the exception type.
fukasawa e60969
fukasawa e60969
fukasawa e60969
struct exception_context;
fukasawa e60969
fukasawa e60969
    This type may be used after the define_exception_type() macro has
fukasawa e60969
    been invoked.  A struct exception_context must be known to both
fukasawa e60969
    the thrower and the catcher.  It is expected that there be one
fukasawa e60969
    context for each thread that uses exceptions.  It would certainly
fukasawa e60969
    be dangerous for multiple threads to access the same context.
fukasawa e60969
    One thread can use multiple contexts, but that is likely to be
fukasawa e60969
    confusing and not typically useful.  The application can allocate
fukasawa e60969
    this structure in any way it pleases--automatic, static, or dynamic.
fukasawa e60969
    The application programmer should pretend not to know the structure
fukasawa e60969
    members, which are subject to change.
fukasawa e60969
fukasawa e60969
fukasawa e60969
struct exception_context *the_exception_context;
fukasawa e60969
fukasawa e60969
    The Try/Catch and Throw statements (described below) implicitly
fukasawa e60969
    refer to a context, using the name the_exception_context.  It is
fukasawa e60969
    the application's responsibility to make sure that this name yields
fukasawa e60969
    the address of a mutable (non-constant) struct exception_context
fukasawa e60969
    wherever those statements are used.  Subject to that constraint, the
fukasawa e60969
    application may declare a variable of this name anywhere it likes
fukasawa e60969
    (inside a function, in a parameter list, or externally), and may
fukasawa e60969
    use whatever storage class specifiers (static, extern, etc) or type
fukasawa e60969
    qualifiers (const, volatile, etc) it likes.  Examples:
fukasawa e60969
fukasawa e60969
        static struct exception_context
fukasawa e60969
          * const the_exception_context = &foo;
fukasawa e60969
fukasawa e60969
        { struct exception_context *the_exception_context = bar; ... }
fukasawa e60969
fukasawa e60969
        int blah(struct exception_context *the_exception_context, ...);
fukasawa e60969
fukasawa e60969
        extern struct exception_context the_exception_context[1];
fukasawa e60969
fukasawa e60969
    The last example illustrates a trick that avoids creating a pointer
fukasawa e60969
    object separate from the structure object.
fukasawa e60969
fukasawa e60969
    The name could even be a macro, for example:
fukasawa e60969
fukasawa e60969
        struct exception_context ec_array[numthreads];
fukasawa e60969
        #define the_exception_context (ec_array + thread_id)
fukasawa e60969
fukasawa e60969
    Be aware that the_exception_context is used several times by the
fukasawa e60969
    Try/Catch/Throw macros, so it shouldn't be expensive or have side
fukasawa e60969
    effects.  The expansion must be a drop-in replacement for an
fukasawa e60969
    identifier, so it's safest to put parentheses around it.
fukasawa e60969
fukasawa e60969
fukasawa e60969
void init_exception_context(struct exception_context *ec);
fukasawa e60969
fukasawa e60969
    For context structures allocated statically (by an external
fukasawa e60969
    definition or using the "static" keyword), the implicit
fukasawa e60969
    initialization to all zeros is sufficient, but contexts allocated
fukasawa e60969
    by other means must be initialized using this macro before they
fukasawa e60969
    are used by a Try/Catch statement.  It does no harm to initialize
fukasawa e60969
    a context more than once (by using this macro on a statically
fukasawa e60969
    allocated context, or using this macro twice on the same context),
fukasawa e60969
    but a context must not be re-initialized after it has been used by a
fukasawa e60969
    Try/Catch statement.
fukasawa e60969
fukasawa e60969
fukasawa e60969
Try statement
fukasawa e60969
Catch (expression) statement
fukasawa e60969
fukasawa e60969
    The Try/Catch/Throw macros are capitalized in order to avoid
fukasawa e60969
    confusion with the C++ keywords, which have subtly different
fukasawa e60969
    semantics.
fukasawa e60969
fukasawa e60969
    A Try/Catch statement has a syntax similar to an if/else statement,
fukasawa e60969
    except that the parenthesized expression goes after the second
fukasawa e60969
    keyword rather than the first.  As with if/else, there are two
fukasawa e60969
    clauses, each of which may be a simple statement ending with a
fukasawa e60969
    semicolon or a brace-enclosed compound statement.  But whereas
fukasawa e60969
    the else clause is optional, the Catch clause is required.  The
fukasawa e60969
    expression must be a modifiable lvalue (something capable of being
fukasawa e60969
    assigned to) of the same type (disregarding type qualifiers) that
fukasawa e60969
    was passed to define_exception_type().
fukasawa e60969
fukasawa e60969
    If a Throw that uses the same exception context as the Try/Catch is
fukasawa e60969
    executed within the Try clause (typically within a function called
fukasawa e60969
    by the Try clause), and the exception is not caught by a nested
fukasawa e60969
    Try/Catch statement, then a copy of the exception will be assigned
fukasawa e60969
    to the expression, and control will jump to the Catch clause.  If no
fukasawa e60969
    such Throw is executed, then the assignment is not performed, and
fukasawa e60969
    the Catch clause is not executed.
fukasawa e60969
fukasawa e60969
    The expression is not evaluated unless and until the exception is
fukasawa e60969
    caught, which is significant if it has side effects, for example:
fukasawa e60969
fukasawa e60969
        Try foo();
fukasawa e60969
        Catch (p[++i].e) { ... }
fukasawa e60969
fukasawa e60969
    IMPORTANT: Jumping into or out of a Try clause (for example via
fukasawa e60969
    return, break, continue, goto, longjmp) is forbidden--the compiler
fukasawa e60969
    will not complain, but bad things will happen at run-time.  Jumping
fukasawa e60969
    into or out of a Catch clause is okay, and so is jumping around
fukasawa e60969
    inside a Try clause.  In many cases where one is tempted to return
fukasawa e60969
    from a Try clause, it will suffice to use Throw, and then return
fukasawa e60969
    from the Catch clause.  Another option is to set a flag variable and
fukasawa e60969
    use goto to jump to the end of the Try clause, then check the flag
fukasawa e60969
    after the Try/Catch statement.
fukasawa e60969
fukasawa e60969
    IMPORTANT: The values of any non-volatile automatic variables
fukasawa e60969
    changed within the Try clause are undefined after an exception is
fukasawa e60969
    caught.  Therefore, variables modified inside the Try block whose
fukasawa e60969
    values are needed later outside the Try block must either use static
fukasawa e60969
    storage or be declared with the "volatile" type qualifier.
fukasawa e60969
fukasawa e60969
fukasawa e60969
Throw expression;
fukasawa e60969
fukasawa e60969
    A Throw statement is very much like a return statement, except that
fukasawa e60969
    the expression is required.  Whereas return jumps back to the place
fukasawa e60969
    where the current function was called, Throw jumps back to the Catch
fukasawa e60969
    clause of the innermost enclosing Try clause.  The expression must
fukasawa e60969
    be compatible with the type passed to define_exception_type().  The
fukasawa e60969
    exception must be caught, otherwise the program may crash.
fukasawa e60969
fukasawa e60969
    Slight limitation:  If the expression is a comma-expression, it must
fukasawa e60969
    be enclosed in parentheses.
fukasawa e60969
fukasawa e60969
fukasawa e60969
Try statement
fukasawa e60969
Catch_anonymous statement
fukasawa e60969
fukasawa e60969
    When the value of the exception is not needed, a Try/Catch statement
fukasawa e60969
    can use Catch_anonymous instead of Catch (expression).
fukasawa e60969
fukasawa e60969
fukasawa e60969
Everything below this point is for the benefit of the compiler.  The
fukasawa e60969
application programmer should pretend not to know any of it, because it
fukasawa e60969
is subject to change.
fukasawa e60969
fukasawa e60969
===*/
fukasawa e60969
fukasawa e60969
fukasawa e60969
#ifndef CEXCEPT_H
fukasawa e60969
#define CEXCEPT_H
fukasawa e60969
fukasawa e60969
fukasawa e60969
#include <setjmp.h></setjmp.h>
fukasawa e60969
fukasawa e60969
#define define_exception_type(etype) \
fukasawa e60969
struct exception_context { \
fukasawa e60969
  jmp_buf *penv; \
fukasawa e60969
  int caught; \
fukasawa e60969
  volatile struct { etype etmp; } v; \
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
/* etmp must be volatile because the application might use automatic */
fukasawa e60969
/* storage for the_exception_context, and etmp is modified between   */
fukasawa e60969
/* the calls to setjmp() and longjmp().  A wrapper struct is used to */
fukasawa e60969
/* avoid warnings about a duplicate volatile qualifier in case etype */
fukasawa e60969
/* already includes it.                                              */
fukasawa e60969
fukasawa e60969
#define init_exception_context(ec) ((void)((ec)->penv = 0))
fukasawa e60969
fukasawa e60969
#define Try \
fukasawa e60969
  { \
fukasawa e60969
    jmp_buf *exception__prev, exception__env; \
fukasawa e60969
    exception__prev = the_exception_context->penv; \
fukasawa e60969
    the_exception_context->penv = &exception__env; \
fukasawa e60969
    if (setjmp(exception__env) == 0) { \
fukasawa e60969
      do
fukasawa e60969
fukasawa e60969
#define exception__catch(action) \
fukasawa e60969
      while (the_exception_context->caught = 0, \
fukasawa e60969
             the_exception_context->caught); \
fukasawa e60969
    } \
fukasawa e60969
    else { \
fukasawa e60969
      the_exception_context->caught = 1; \
fukasawa e60969
    } \
fukasawa e60969
    the_exception_context->penv = exception__prev; \
fukasawa e60969
  } \
fukasawa e60969
  if (!the_exception_context->caught || action) { } \
fukasawa e60969
  else
fukasawa e60969
fukasawa e60969
#define Catch(e) exception__catch(((e) = the_exception_context->v.etmp, 0))
fukasawa e60969
#define Catch_anonymous exception__catch(0)
fukasawa e60969
fukasawa e60969
/* Try ends with do, and Catch begins with while(0) and ends with     */
fukasawa e60969
/* else, to ensure that Try/Catch syntax is similar to if/else        */
fukasawa e60969
/* syntax.                                                            */
fukasawa e60969
/*                                                                    */
fukasawa e60969
/* The 0 in while(0) is expressed as x=0,x in order to appease        */
fukasawa e60969
/* compilers that warn about constant expressions inside while().     */
fukasawa e60969
/* Most compilers should still recognize that the condition is always */
fukasawa e60969
/* false and avoid generating code for it.                            */
fukasawa e60969
fukasawa e60969
#define Throw \
fukasawa e60969
  for (;; longjmp(*the_exception_context->penv, 1)) \
fukasawa e60969
    the_exception_context->v.etmp =
fukasawa e60969
fukasawa e60969
fukasawa e60969
#endif /* CEXCEPT_H */