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