chiark / gitweb /
a2e004e8df486d469f807192a51998e23b87fafe
[mLib] / exc.h
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
129 typedef 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
177 typedef 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
235 typedef 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
244 extern __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
307 typedef void (*exc__uncaught)(exc_extype /*type*/, exc_exval /*val*/);
308 extern 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
320 extern 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
333 extern 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