cexcept.h

Go to the documentation of this file.
00001 /*===
00002 cexcept.h 1.0.0 (2000-Jun-21-Wed)
00003 Adam M. Costello <amc@cs.berkeley.edu>
00004 
00005 An interface for exception-handling in ANSI C, developed jointly with
00006 Cosmin Truta <cosmin@cs.toronto.edu>.
00007 
00008     Copyright (c) 2000 Adam M. Costello and Cosmin Truta.  Everyone
00009     is hereby granted permission to do whatever they like with this
00010     file, provided that if they modify it they take reasonable steps to
00011     avoid confusing or misleading people about the authors, version,
00012     and terms of use of the derived file.  The copyright holders make
00013     no guarantees about the correctness of this file, and are not
00014     responsible for any damage resulting from its use.
00015 
00016 If this interface is used by multiple .c files, they shouldn't include
00017 this header file directly.  Instead, create a wrapper header file that
00018 includes this header file and then invokes the define_exception_type
00019 macro (see below), and let your .c files include that header file.
00020 
00021 The interface consists of one type, one well-known name, and six macros.
00022 
00023 
00024 define_exception_type(type_name);
00025 
00026     This macro is used like an external declaration.  It specifies
00027     the type of object that gets copied from the exception thrower to
00028     the exception catcher.  The type_name can be any type that can be
00029     assigned to, that is, a non-constant arithmetic type, struct, union,
00030     or pointer.  Examples:
00031 
00032         define_exception_type(int);
00033 
00034         enum exception { out_of_memory, bad_arguments, disk_full };
00035         define_exception_type(enum exception);
00036 
00037         struct exception { int code; const char *msg; };
00038         define_exception_type(struct exception);
00039 
00040 
00041 struct exception_context;
00042 
00043     This type may be used after the define_exception_type() macro has
00044     been invoked.  A struct exception_context must be known to both
00045     the thrower and the catcher.  It is expected that there be one
00046     context for each thread that uses exceptions.  It would certainly
00047     be dangerous for multiple threads to access the same context.
00048     One thread can use multiple contexts, but that is likely to be
00049     confusing and not typically useful.  The application can allocate
00050     this structure in any way it pleases--automatic, static, or dynamic.
00051     The application programmer should pretend not to know the structure
00052     members, which are subject to change.
00053 
00054 
00055 struct exception_context *the_exception_context;
00056 
00057     The Try/Catch and Throw statements (described below) implicitly
00058     refer to a context, using the name the_exception_context.  It is
00059     the application's responsibility to make sure that this name yields
00060     the address of a mutable (non-constant) struct exception_context
00061     wherever those statements are used.  Subject to that constraint, the
00062     application may declare a variable of this name anywhere it likes
00063     (inside a function, in a parameter list, or externally), and may
00064     use whatever storage class specifiers (static, extern, etc) or type
00065     qualifiers (const, volatile) it likes.  Examples:
00066 
00067         static struct exception_context
00068           * const the_exception_context = &foo;
00069 
00070         { struct exception_context *the_exception_context = bar; ... }
00071 
00072         int blah(struct exception_context *the_exception_context, ...);
00073 
00074         extern struct exception_context the_exception_context[1];
00075 
00076     The last example illustrates a trick that avoids creating a pointer
00077     object separate from the structure object.
00078 
00079     The name could even be a macro, for example:
00080 
00081         struct exception_context ec_array[numthreads];
00082         #define the_exception_context (ec_array + thread_id)
00083 
00084     Be aware that the_exception_context is used several times by the
00085     Try/Catch/Throw macros, so it shouldn't be expensive or have side
00086     effects.  The expansion must be a drop-in replacement for an
00087     identifier, so it's safest to put parentheses around it.
00088 
00089 
00090 void init_exception_context(struct exception_context *ec);
00091 
00092     For context structures allocated statically (by an external
00093     definition or using the "static" keyword), the implicit
00094     initialization to all zeros is sufficient, but contexts allocated
00095     by other means must be initialized using this macro before they
00096     are used by a Try/Catch statement.  It does no harm to initialize
00097     a context more than once (by using this macro on a statically
00098     allocated context, or using this macro twice on the same context),
00099     but a context must not be re-initialized after it has been used by a
00100     Try/Catch statement.
00101 
00102 
00103 Try statement
00104 Catch (expression) statement
00105 
00106     The Try/Catch/Throw macros are capitalized in order to avoid
00107     confusion with the C++ keywords, which have subtly different
00108     semantics.
00109 
00110     A Try/Catch statement has a syntax similar to an if/else
00111     statement, except that the parenthesized expression goes after
00112     the second keyword rather than the first.  As with if/else,
00113     there are two clauses, each of which may be a simple statement
00114     ending with a semicolon or a brace-enclosed compound statement.
00115     But whereas the else clause is optional, the Catch clause is
00116     required.  The expression must be a modifiable lvalue (something
00117     capable of being assigned to) of the exact same type passed to
00118     define_exception_type().
00119 
00120     If a Throw that uses the same exception context as the Try/Catch is
00121     executed within the Try clause (typically within a function called
00122     by the Try clause), and the exception is not caught by a nested
00123     Try/Catch statement, then a copy of the exception will be assigned
00124     to the expression, and control will jump to the Catch clause.  If no
00125     such Throw is executed, then the assignment is not performed, and
00126     the Catch clause is not executed.
00127 
00128     Regardless of whether an exception is caught, the expression is
00129     always evaluated exactly once, which is significant if it has side
00130     effects, for example:
00131 
00132         Try foo();
00133         Catch (p[++i].e) { ... }
00134 
00135     IMPORTANT: Jumping into or out of a Try clause (for example via
00136     return, break, continue, goto, longjmp) is forbidden--the compiler
00137     will not complain, but bad things will happen at run-time.  Jumping
00138     into or out of a Catch clause is okay, and so is jumping around
00139     inside a Try clause.  In many cases where one is tempted to return
00140     from a Try clause, it will suffice to use Throw, and then return
00141     from the Catch clause.  Another option is to set a flag variable and
00142     use goto to jump to the end of the Try clause, then check the flag
00143     after the Try/Catch statement.
00144 
00145     IMPORTANT: The values of any non-volatile automatic variables
00146     changed within the Try clause are undefined after an exception is
00147     caught.  Therefore, variables modified inside the Try block whose
00148     values are needed later outside the Try block must either use static
00149     storage or be declared with the "volatile" type qualifier.
00150 
00151 
00152 Throw expression;
00153 
00154     A Throw statement is very much like a return statement, except that
00155     the expression is required.  Whereas return jumps back to the place
00156     where the current function was called, Throw jumps back to the Catch
00157     clause of the innermost enclosing Try clause.  The expression must
00158     be compatible with the type passed to define_exception_type().  The
00159     exception must be caught, otherwise the program may crash.
00160 
00161     Slight limitation:  If the expression is a comma-expression it must
00162     be enclosed in parentheses.
00163 
00164 
00165 Try statement
00166 Catch_anonymous statement
00167 
00168     When the value of the exception is not needed, a Try/Catch statement
00169     can use Catch_anonymous instead of Catch (expression).
00170 
00171 
00172 Everything below this point is for the benefit of the compiler.  The
00173 application programmer should pretend not to know any of it, because it
00174 is subject to change.
00175 
00176 ===*/
00177 
00178 
00179 #ifndef CEXCEPT_H
00180 #define CEXCEPT_H
00181 
00182 
00183 #include <setjmp.h>
00184 
00185 #define define_exception_type(etype) \
00186 struct exception__state { \
00187   etype *exception; \
00188   jmp_buf env; \
00189 }
00190 
00191 struct exception_context { \
00192   struct exception__state *last; \
00193   int caught; \
00194 };
00195 
00196 #define init_exception_context(ec) ((void)((ec)->last = 0))
00197 
00198 #define Catch(e) exception__catch(&(e))
00199 
00200 #ifndef NOCOMP
00201 #define Catch_anonymous exception__catch(0)
00202 #endif
00203 
00204 #ifndef NOCOMP
00205 #define Try \
00206   { \
00207     struct exception__state *exception__p, exception__s; \
00208     int exception__i; \
00209     exception__p = the_exception_context->last; \
00210     the_exception_context->last = &exception__s; \
00211     for (exception__i = 0; ; exception__i = 1) \
00212       if (exception__i) { \
00213         if (setjmp(exception__s.env) == 0) { \
00214           if (&exception__s)
00215 #endif
00216 
00217 #define exception__catch(e_addr) \
00218           else { } \
00219           the_exception_context->caught = 0; \
00220         } \
00221         else the_exception_context->caught = 1; \
00222         the_exception_context->last = exception__p; \
00223         break; \
00224       } \
00225       else exception__s.exception = e_addr; \
00226   } \
00227   if (!the_exception_context->caught) { } \
00228   else
00229 
00230 /* Try ends with if(), and Catch begins and ends with else.  This     */
00231 /* ensures that the Try/Catch syntax is really the same as the        */
00232 /* if/else syntax.                                                    */
00233 /*                                                                    */
00234 /* We use &exception__s instead of 1 to appease compilers that        */
00235 /* warn about constant expressions inside if().  Most compilers       */
00236 /* should still recognize that &exception__s is never zero and avoid  */
00237 /* generating test code.                                              */
00238 /*                                                                    */
00239 /* We use the variable exception__i to start the loop at the bottom,  */
00240 /* rather than jump into the loop using a switch statement, to        */
00241 /* appease compilers that warn about jumping into loops.              */
00242 
00243 #ifndef NOCOMP
00244 #define Throw \
00245   for (;; longjmp(the_exception_context->last->env, 1)) \
00246     if (the_exception_context->last->exception) \
00247       *the_exception_context->last->exception =
00248 #endif
00249 
00250 
00251 #endif /* CEXCEPT_H */
Generated on Sun Nov 21 00:55:34 2010 for Covered by  doxygen 1.6.3