chiark / gitweb /
*** empty log message ***
[mLib] / exc.h
CommitLineData
0875b58f 1/* -*-c-*-
2 *
3 * $Id: exc.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
4 *
5 * Structured exception handling in C
6 *
7 * (c) 1998 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the mLib utilities library.
13 *
14 * mLib is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * mLib is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with mLib; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: exc.h,v $
32 * Revision 1.1 1998/06/17 23:44:42 mdw
33 * Initial revision
34 *
35 */
36
37#ifndef EXC_H
38#define EXC_H
39
40#ifdef __cplusplus
41 extern "C" {
42#endif
43
44#include <setjmp.h>
45
46/*----- Quick documentation -----------------------------------------------*
47 *
48 * This header file provides some exception handling facilities in C
49 * programs. It modifies the syntax of the language slightly, using the
50 * preprocessor.
51 *
52 * The `throw' expression returns no value. It has the syntax:
53 *
54 * THROW ( expr , expr )
55 *
56 * The first expression must have type compatible with unsigned integer; it
57 * identifies an `exception type'. The second must have type compatible
58 * with pointer to void; it contains the `exception data'. Control is
59 * passed to the current exception handler.
60 *
61 * The `RETHROW' expression, valid only within an exception handler, causes
62 * the current exception to be thrown again.
63 *
64 * A `try' statement has the syntax:
65 *
66 * TRY stat CATCH stat END_TRY;
67 *
68 * The first statement is called the `test'; the second is the `handler'.
69 * During execution of the test, the handler is added to a stack of
70 * active exception handlers; the topmost handler on this stack is called
71 * the `current' handler. When execution of the test completes, the
72 * corresponding handler is removed from the stack.
73 *
74 * The test statement may complete in one of these ways:
75 *
76 * * Normal completion -- control reaches the end of the statement
77 * normally.
78 *
79 * * Throwing an exception -- an exception is thrown when the handler is
80 * the current exception handler.
81 *
82 * * By executing a `break' statement.
83 *
84 * * By executing the expression `EXIT_TRY' and transferring control to
85 * a point outside the entire `try' statement (e.g., executing a `goto'
86 * or `return' statement).
87 *
88 * Any other attempt to leave the test causes undefined behaviour.
89 *
90 * If an exception is thrown while the handler is the current exception
91 * handler, it is given control. The variables `exc_type' and `exc_val'
92 * denote the exception type and value respectively -- they are passed
93 * unchanged from the `throw' expression which caused the exception.
94 * A handler is deactivated before it is invoked; if it causes an
95 * exception to be thrown (and does not contain a nested `try' statement)
96 * control will be passed to an earlier active handler.
97 *
98 * Control is passed to handlers using the `longjmp' function.
99 *
100 * Example:
101 *
102 * TRY {
103 * ... something dangerous ...
104 * } CATCH switch (exc_type) {
105 * case EXC_INTERESTING:
106 * ... handle exception ...
107 * break;
108 * default:
109 * ... do tidying up ...
110 * RETHROW;
111 * } END_TRY;
112 */
113
114/*----- Exception type allocation -----------------------------------------*
115 *
116 * Nobody allocates exception types, so we'll just have to try to get along
117 * without too many collisions. An exception type is an unsigned long,
118 * which gives us four bytes. The top two bytes identify the library which
119 * `owns' the exception, with special values zero meaning `defined as part
120 * of the system' and 0xFFFF providing a shared space of types which can
121 * be used by anyone as long as they don't get seen by anyone else.
122 *
123 * The lower byte pair encodes a type number, and a value which defines
124 * the type of the value field (see below).
125 */
126
127/* --- Type type of an exception --- */
128
129typedef unsigned long exc_extype;
130
131/* --- Build a byte pair from two characters --- *
132 *
133 * Note the icky casting to handle signed chars.
134 */
135
136#define EXC_PAIR(x, y) (((unsigned long)(unsigned char)(x) << 8) | \
137 (unsigned long)(unsigned char)(y))
138
139/* --- Allocate an exception number --- */
140
141#define EXC_ALLOC(owner, type) (((unsigned long)(owner) << 16) | \
142 (unsigned long)(type))
143
144/* --- Special owner codes --- */
145
146#define EXC_GLOBAL 0u /* The global space defined here */
147#define EXC_SHARED 0xFFFFu /* The shared space for everyone */
148
149/*----- Exception values --------------------------------------------------*
150 *
151 * Exception values can have several different types. This is a mess, and
152 * C doesn't handle it too well, but we can try. I'll encode the value type
153 * as part of the exception type, in the top bits of the bottom byte. Messy?
154 * You betcha.
155 */
156
157/* --- Encoding a value type in an extype --- */
158
159#define EXC_TYPECODE(t, w) (((w) & ~0xC0u) | ((t) & 0xC0u))
160
161/* --- The various value types --- */
162
163#define EXC_NOVAL 0x00u /* No interesting value */
164#define EXC_INTVAL 0x40u /* Integer value */
165#define EXC_PTRVAL 0x80u /* Arbitrary pointer value */
166#define EXC_STRVAL 0xC0u /* Pointer to character string */
167
168/* --- Allocating exceptions with appropriate types --- */
169
170#define EXC_ALLOCN(o, t) EXC_TYPECODE(EXC_NOVAL, EXC_ALLOC(o, t))
171#define EXC_ALLOCI(o, t) EXC_TYPECODE(EXC_INTVAL, EXC_ALLOC(o, t))
172#define EXC_ALLOCP(o, t) EXC_TYPECODE(EXC_PTRVAL, EXC_ALLOC(o, t))
173#define EXC_ALLOCS(o, t) EXC_TYPECODE(EXC_STRVAL, EXC_ALLOC(o, t))
174
175/* --- A union representing the type --- */
176
177typedef union exc_exval {
178 int i;
179 void *p;
180 char *s;
181} exc_exval;
182
183/*----- Predefined exceptions ---------------------------------------------*/
184
185/* --- @EXC_NOMEM@ --- *
186 *
187 * Value: ---
188 *
189 * Meaning: An attempt to allocate memory failed.
190 */
191
192#define EXC_NOMEM EXC_ALLOCN(EXC_GLOBAL, 0u)
193
194/* --- @EXC_ERRNO@ --- *
195 *
196 * Value: @int errno@ = the error raised
197 *
198 * Meaning: Some kind of OS error occurred.
199 */
200
201#define EXC_ERRNO EXC_ALLOCI(EXC_GLOBAL, 1u)
202
203/* --- @EXC_OSERROR@ --- *
204 *
205 * Value: @os_error *e@ = pointer to error block
206 *
207 * Meaning: For RISC OS programmers only: alternative way of propagating
208 * errors.
209 */
210
211#define EXC_OSERROR EXC_ALLOCP(EXC_GLOBAL, 1u)
212
213/* --- @EXC_SIGNAL@ --- *
214 *
215 * Value: @int sig@ = signal number
216 *
217 * Meaning: Report the raising of a signal.
218 */
219
220#define EXC_SIGNAL EXC_ALLOCI(EXC_GLOBAL, 2u)
221
222/* --- @EXC_FAIL@ --- *
223 *
224 * Value: @const char *p@ = pointer to expanatory string
225 *
226 * Meaning: Miscellaneous error.
227 */
228
229#define EXC_FAIL EXC_ALLOCS(EXC_GLOBAL, 0xFFu)
230
231/*----- An exception handler block ----------------------------------------*/
232
233/* --- Try to think of this as being opaque --- */
234
235typedef struct __exc_hnd {
236 struct __exc_hnd *next; /* Pointer to next record down */
237 exc_extype type; /* Type of this exception */
238 exc_exval val; /* Value of this exception */
239 jmp_buf buf; /* Jump buffer when exceptions hit */
240} __exc_hnd;
241
242/*----- Global variables --------------------------------------------------*/
243
244extern __exc_hnd *__exc_list; /* List of active handlers */
245
246/*----- Macros ------------------------------------------------------------*/
247
248/* --- References to current exception type and value --- */
249
250#define exc_type (__exc_ec.type)
251#define exc_val (__exc_ec.val)
252#define exc_i (__exc_ec.val.i)
253#define exc_p (__exc_ec.val.p)
254#define exc_s (__exc_ec.val.s)
255
256/* --- How it actually works --- *
257 *
258 * A `try' block is contained within a block which provides an exception
259 * handler buffer in automatic storage. This block is a loop, to allow
260 * `break' to escape from it. It adds the handler buffer to the top of a
261 * list, and does a `setjmp' to allow a return here following an exception.
262 * The `setjmp' returns zero for the `try' section, and nonzero if there's
263 * an exception to `catch'. It looks a little like this:
264 *
265 * do {
266 * __exc_hnd h;
267 * add_handler(&h);
268 * if (!setjmp(h.buf)) {
269 * do <try code> while (0);
270 * remove_handler(&h);
271 * } else
272 * <catch code>
273 * } while (0)
274 *
275 * Everything else is ugly hacking to make things work.
276 */
277
278/* --- Trying things which may cause exceptions --- */
279
280#define TRY do { \
281 volatile __exc_hnd __exc_ec; \
282 __exc_ec.next = __exc_list; \
283 __exc_list = (__exc_hnd *)&__exc_ec; \
284 if (!setjmp(*(jmp_buf *)&__exc_ec.buf /* very nasty! */ )) { do
285
286#define EXIT_TRY do __exc_list = __exc_ec.next; while (0)
287#define CATCH while (0); EXIT_TRY; } else
288
289#define END_TRY } while (0)
290
291/* --- Raising exceptions --- */
292
293#define THROW __exc_throw
294#define RETHROW __exc_rethrow(__exc_ec.type, __exc_ec.val)
295
296/*----- Functions ---------------------------------------------------------*/
297
298/* --- @exc_uncaught@ --- *
299 *
300 * Arguments: @void (*proc)(exc_extype type, exc_exval val) = new handler
301 *
302 * Returns: Pointer to the old handler value.
303 *
304 * Use: Sets the handler for uncaught exceptions.
305 */
306
307typedef void (*exc__uncaught)(exc_extype /*type*/, exc_exval /*val*/);
308extern exc__uncaught exc_uncaught(exc__uncaught /*proc*/);
309
310/* --- @__exc_throw@ --- *
311 *
312 * Arguments: @exc_extype type@ = type of exception to throw
313 *
314 * Returns: Doesn't
315 *
316 * Use: NOT FOR USER CONSUMPTION. Reads an appropriate exception
317 * value and throws an exception.
318 */
319
320extern void __exc_throw(exc_extype /*type*/, ...);
321
322/* --- @__exc_rethrow@ --- *
323 *
324 * Arguments: @exc_extype type@ = type of exception to throw
325 * @exc_exval val@ = value of exception to throw
326 *
327 * Returns: Doesn't
328 *
329 * Use: NOT FOR USER CONSUMPTION. Does the donkey-work of raising
330 * an exception.
331 */
332
333extern void __exc_rethrow(exc_extype /*type*/, exc_exval /*val*/);
334
335/*----- That's all, folks -------------------------------------------------*/
336
337#ifdef __cplusplus
338 }
339#endif
340
341#endif