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