1 /*=== 2 cexcept.h 2.0.1-Lichen (2016-Oct-27-Thu) 3 A modified form of... 4 cexcept.h 2.0.1 (2008-Jul-19-Sat) 5 http://www.nicemice.net/cexcept/ 6 Adam M. Costello 7 http://www.nicemice.net/amc/ 8 9 An interface for exception-handling in ANSI C (C89 and subsequent ISO 10 standards), developed jointly with Cosmin Truta. 11 12 Copyright (c) 2016 Paul Boddie (modified for Lichen). 13 Copyright (c) 2000-2008 Adam M. Costello and Cosmin Truta. 14 This software may be modified only if its author and version 15 information is updated accurately, and may be redistributed 16 only if accompanied by this unaltered notice. Subject to those 17 restrictions, permission is granted to anyone to do anything 18 with this software. The copyright holders make no guarantees 19 regarding this software, and are not responsible for any damage 20 resulting from its use. 21 22 The cexcept interface is not compatible with and cannot interact 23 with system exceptions (like division by zero or memory segmentation 24 violation), compiler-generated exceptions (like C++ exceptions), or 25 other exception-handling interfaces. 26 27 When using this interface across multiple .c files, do not include 28 this header file directly. Instead, create a wrapper header file that 29 includes this header file and then invokes the define_exception_type 30 macro (see below). The .c files should then include that header file. 31 32 The interface consists of one type, one well-known name, and six macros. 33 34 35 define_exception_type(type_name); 36 37 This macro is used like an external declaration. It specifies 38 the type of object that gets copied from the exception thrower to 39 the exception catcher. The type_name can be any type that can be 40 assigned to, that is, a non-constant arithmetic type, struct, union, 41 or pointer. Examples: 42 43 define_exception_type(int); 44 45 enum exception { out_of_memory, bad_arguments, disk_full }; 46 define_exception_type(enum exception); 47 48 struct exception { int code; const char *msg; }; 49 define_exception_type(struct exception); 50 51 Because throwing an exception causes the object to be copied (not 52 just once, but twice), programmers may wish to consider size when 53 choosing the exception type. 54 55 56 struct __exception_context; 57 58 This type may be used after the define_exception_type() macro has 59 been invoked. A struct __exception_context must be known to both 60 the thrower and the catcher. It is expected that there be one 61 context for each thread that uses exceptions. It would certainly 62 be dangerous for multiple threads to access the same context. 63 One thread can use multiple contexts, but that is likely to be 64 confusing and not typically useful. The application can allocate 65 this structure in any way it pleases--automatic, static, or dynamic. 66 The application programmer should pretend not to know the structure 67 members, which are subject to change. 68 69 70 struct __exception_context *__the_exception_context; 71 72 The __Try/__Catch and __Throw statements (described below) implicitly 73 refer to a context, using the name __the_exception_context. It is 74 the application's responsibility to make sure that this name yields 75 the address of a mutable (non-constant) struct __exception_context 76 wherever those statements are used. Subject to that constraint, the 77 application may declare a variable of this name anywhere it likes 78 (inside a function, in a parameter list, or externally), and may 79 use whatever storage class specifiers (static, extern, etc) or type 80 qualifiers (const, volatile, etc) it likes. Examples: 81 82 static struct __exception_context 83 * const __the_exception_context = &foo; 84 85 { struct __exception_context *__the_exception_context = bar; ... } 86 87 int blah(struct __exception_context *__the_exception_context, ...); 88 89 extern struct __exception_context __the_exception_context[1]; 90 91 The last example illustrates a trick that avoids creating a pointer 92 object separate from the structure object. 93 94 The name could even be a macro, for example: 95 96 struct __exception_context ec_array[numthreads]; 97 #define __the_exception_context (ec_array + thread_id) 98 99 Be aware that __the_exception_context is used several times by the 100 __Try/__Catch/__Throw macros, so it shouldn't be expensive or have side 101 effects. The expansion must be a drop-in replacement for an 102 identifier, so it's safest to put parentheses around it. 103 104 105 void __init_exception_context(struct __exception_context *ec); 106 107 For context structures allocated statically (by an external 108 definition or using the "static" keyword), the implicit 109 initialization to all zeros is sufficient, but contexts allocated 110 by other means must be initialized using this macro before they 111 are used by a __Try/__Catch statement. It does no harm to initialize 112 a context more than once (by using this macro on a statically 113 allocated context, or using this macro twice on the same context), 114 but a context must not be re-initialized after it has been used by a 115 __Try/__Catch statement. 116 117 118 __Try statement 119 __Catch (expression) statement 120 121 The __Try/__Catch/__Throw macros are capitalized in order to avoid 122 confusion with the C++ keywords, which have subtly different 123 semantics. 124 125 A __Try/__Catch statement has a syntax similar to an if/else statement, 126 except that the parenthesized expression goes after the second 127 keyword rather than the first. As with if/else, there are two 128 clauses, each of which may be a simple statement ending with a 129 semicolon or a brace-enclosed compound statement. But whereas 130 the else clause is optional, the __Catch clause is required. The 131 expression must be a modifiable lvalue (something capable of being 132 assigned to) of the same type (disregarding type qualifiers) that 133 was passed to define_exception_type(). 134 135 If a __Throw that uses the same exception context as the __Try/__Catch is 136 executed within the __Try clause (typically within a function called 137 by the __Try clause), and the exception is not caught by a nested 138 __Try/__Catch statement, then a copy of the exception will be assigned 139 to the expression, and control will jump to the __Catch clause. If no 140 such __Throw is executed, then the assignment is not performed, and 141 the __Catch clause is not executed. 142 143 The expression is not evaluated unless and until the exception is 144 caught, which is significant if it has side effects, for example: 145 146 __Try foo(); 147 __Catch (p[++i].e) { ... } 148 149 IMPORTANT: Jumping into or out of a __Try clause (for example via 150 return, break, continue, goto, longjmp) is forbidden--the compiler 151 will not complain, but bad things will happen at run-time. Jumping 152 into or out of a __Catch clause is okay, and so is jumping around 153 inside a __Try clause. In many cases where one is tempted to return 154 from a __Try clause, it will suffice to use __Throw, and then return 155 from the __Catch clause. Another option is to set a flag variable and 156 use goto to jump to the end of the __Try clause, then check the flag 157 after the __Try/__Catch statement. 158 159 IMPORTANT: The values of any non-volatile automatic variables 160 changed within the __Try clause are undefined after an exception is 161 caught. Therefore, variables modified inside the __Try block whose 162 values are needed later outside the __Try block must either use static 163 storage or be declared with the "volatile" type qualifier. 164 165 166 __Throw expression; 167 168 A __Throw statement is very much like a return statement, except that 169 the expression is required. Whereas return jumps back to the place 170 where the current function was called, __Throw jumps back to the __Catch 171 clause of the innermost enclosing __Try clause. The expression must 172 be compatible with the type passed to define_exception_type(). The 173 exception must be caught, otherwise the program may crash. 174 175 Slight limitation: If the expression is a comma-expression, it must 176 be enclosed in parentheses. 177 178 179 __Try statement 180 __Catch_anonymous statement 181 182 When the value of the exception is not needed, a __Try/__Catch statement 183 can use __Catch_anonymous instead of __Catch (expression). 184 185 186 Everything below this point is for the benefit of the compiler. The 187 application programmer should pretend not to know any of it, because it 188 is subject to change. 189 190 ===*/ 191 192 193 #ifndef CEXCEPT_H 194 #define CEXCEPT_H 195 196 197 #include <setjmp.h> 198 199 #define define_exception_type(etype) \ 200 struct __exception_context { \ 201 jmp_buf *penv; \ 202 int caught; \ 203 volatile struct { etype etmp; } v; \ 204 } 205 206 /* etmp must be volatile because the application might use automatic */ 207 /* storage for __the_exception_context, and etmp is modified between */ 208 /* the calls to setjmp() and longjmp(). A wrapper struct is used to */ 209 /* avoid warnings about a duplicate volatile qualifier in case etype */ 210 /* already includes it. */ 211 212 #define __init_exception_context(ec) ((void)((ec)->penv = 0)) 213 214 #define __Try \ 215 { \ 216 jmp_buf *exception__prev, exception__env; \ 217 exception__prev = __the_exception_context->penv; \ 218 __the_exception_context->penv = &exception__env; \ 219 if (setjmp(exception__env) == 0) { \ 220 do 221 222 #define exception__catch(action) \ 223 while (__the_exception_context->caught = 0, \ 224 __the_exception_context->caught); \ 225 } \ 226 else { \ 227 __the_exception_context->caught = 1; \ 228 } \ 229 __the_exception_context->penv = exception__prev; \ 230 } \ 231 if (!__the_exception_context->caught || action) { } \ 232 else 233 234 #define __Catch(e) exception__catch(((e) = __the_exception_context->v.etmp, 0)) 235 #define __Catch_anonymous exception__catch(0) 236 237 /* __Try ends with do, and __Catch begins with while(0) and ends with */ 238 /* else, to ensure that __Try/__Catch syntax is similar to if/else */ 239 /* syntax. */ 240 /* */ 241 /* The 0 in while(0) is expressed as x=0,x in order to appease */ 242 /* compilers that warn about constant expressions inside while(). */ 243 /* Most compilers should still recognize that the condition is always */ 244 /* false and avoid generating code for it. */ 245 246 #define __Throw \ 247 for (;; longjmp(*__the_exception_context->penv, 1)) \ 248 __the_exception_context->v.etmp = 249 250 251 #endif /* CEXCEPT_H */