chiark / gitweb /
File descriptor passing.
[mLib] / exc.h
CommitLineData
0875b58f 1/* -*-c-*-
2 *
7d40699f 3 * $Id: exc.h,v 1.6 2000/08/07 19:50:18 mdw Exp $
0875b58f 4 *
5 * Structured exception handling in C
6 *
7 * (c) 1998 Straylight/Edgeware
8 */
9
c846879c 10/*----- Licensing notice --------------------------------------------------*
0875b58f 11 *
12 * This file is part of the mLib utilities library.
13 *
14 * mLib is free software; you can redistribute it and/or modify
c846879c 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 *
0875b58f 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
c846879c 22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
0bd98442 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.
0875b58f 28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: exc.h,v $
7d40699f 33 * Revision 1.6 2000/08/07 19:50:18 mdw
34 * Fix typo in comment.
35 *
c6e0eaf0 36 * Revision 1.5 1999/12/10 23:42:04 mdw
37 * Change header file guard names.
38 *
e1ccd441 39 * Revision 1.4 1999/10/22 22:39:18 mdw
40 * Add an exception group for mLib.
41 *
0bd98442 42 * Revision 1.3 1999/05/06 19:51:35 mdw
43 * Reformatted the LGPL notice a little bit.
44 *
c846879c 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
0875b58f 50 *
51 */
52
c6e0eaf0 53#ifndef MLIB_EXC_H
54#define MLIB_EXC_H
0875b58f 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
7d40699f 143/* --- Type of an exception --- */
0875b58f 144
145typedef 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 */
e1ccd441 164#define EXC_MLIB EXC_PAIR('m', 'L') /* Space for mLib exceptions */
0875b58f 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
194typedef 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
252typedef 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
261extern __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
324typedef void (*exc__uncaught)(exc_extype /*type*/, exc_exval /*val*/);
325extern 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
337extern 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
350extern 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