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