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