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 */