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