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