cd3f1c51089fd4996dabe12c49359877a37caad9
3 * Structured exception handling in C
5 * (c) 1998 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
17 * mLib is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
37 /*----- Quick documentation -----------------------------------------------*
39 * This header file provides some exception handling facilities in C
40 * programs. It modifies the syntax of the language slightly, using the
43 * The `throw' expression returns no value. It has the syntax:
45 * THROW ( expr , expr )
47 * The first expression must have type compatible with unsigned integer; it
48 * identifies an `exception type'. The second must have type compatible
49 * with pointer to void; it contains the `exception data'. Control is
50 * passed to the current exception handler.
52 * The `RETHROW' expression, valid only within an exception handler, causes
53 * the current exception to be thrown again.
55 * A `try' statement has the syntax:
57 * TRY stat CATCH stat END_TRY;
59 * The first statement is called the `test'; the second is the `handler'.
60 * During execution of the test, the handler is added to a stack of
61 * active exception handlers; the topmost handler on this stack is called
62 * the `current' handler. When execution of the test completes, the
63 * corresponding handler is removed from the stack.
65 * The test statement may complete in one of these ways:
67 * * Normal completion -- control reaches the end of the statement
70 * * Throwing an exception -- an exception is thrown when the handler is
71 * the current exception handler.
73 * * By executing a `break' statement.
75 * * By executing the expression `EXIT_TRY' and transferring control to
76 * a point outside the entire `try' statement (e.g., executing a `goto'
77 * or `return' statement).
79 * Any other attempt to leave the test causes undefined behaviour.
81 * If an exception is thrown while the handler is the current exception
82 * handler, it is given control. The variables `exc_type' and `exc_val'
83 * denote the exception type and value respectively -- they are passed
84 * unchanged from the `throw' expression which caused the exception.
85 * A handler is deactivated before it is invoked; if it causes an
86 * exception to be thrown (and does not contain a nested `try' statement)
87 * control will be passed to an earlier active handler.
89 * Control is passed to handlers using the `longjmp' function.
94 * ... something dangerous ...
95 * } CATCH switch (exc_type) {
96 * case EXC_INTERESTING:
97 * ... handle exception ...
100 * ... do tidying up ...
105 /*----- Exception type allocation -----------------------------------------*
107 * Nobody allocates exception types, so we'll just have to try to get along
108 * without too many collisions. An exception type is an unsigned long,
109 * which gives us four bytes. The top two bytes identify the library which
110 * `owns' the exception, with special values zero meaning `defined as part
111 * of the system' and 0xFFFF providing a shared space of types which can
112 * be used by anyone as long as they don't get seen by anyone else.
114 * The lower byte pair encodes a type number, and a value which defines
115 * the type of the value field (see below).
118 /* --- Type of an exception --- */
120 typedef unsigned long exc_extype
;
122 /* --- Build a byte pair from two characters --- *
124 * Note the icky casting to handle signed chars.
127 #define EXC_PAIR(x, y) (((unsigned long)(unsigned char)(x) << 8) | \
128 (unsigned long)(unsigned char)(y))
130 /* --- Allocate an exception number --- */
132 #define EXC_ALLOC(owner, type) (((unsigned long)(owner) << 16) | \
133 (unsigned long)(type))
135 /* --- Special owner codes --- */
137 #define EXC_GLOBAL 0u /* The global space defined here */
138 #define EXC_SHARED 0xFFFFu /* The shared space for everyone */
139 #define EXC_MLIB EXC_PAIR('m', 'L') /* Space for mLib exceptions */
141 /*----- Exception values --------------------------------------------------*
143 * Exception values can have several different types. This is a mess, and
144 * C doesn't handle it too well, but we can try. I'll encode the value type
145 * as part of the exception type, in the top bits of the bottom byte. Messy?
149 /* --- Encoding a value type in an extype --- */
151 #define EXC_TYPECODE(t, w) (((w) & ~0xC0u) | ((t) & 0xC0u))
153 /* --- The various value types --- */
155 #define EXC_NOVAL 0x00u /* No interesting value */
156 #define EXC_INTVAL 0x40u /* Integer value */
157 #define EXC_PTRVAL 0x80u /* Arbitrary pointer value */
158 #define EXC_STRVAL 0xC0u /* Pointer to character string */
160 /* --- Allocating exceptions with appropriate types --- */
162 #define EXC_ALLOCN(o, t) EXC_TYPECODE(EXC_NOVAL, EXC_ALLOC(o, t))
163 #define EXC_ALLOCI(o, t) EXC_TYPECODE(EXC_INTVAL, EXC_ALLOC(o, t))
164 #define EXC_ALLOCP(o, t) EXC_TYPECODE(EXC_PTRVAL, EXC_ALLOC(o, t))
165 #define EXC_ALLOCS(o, t) EXC_TYPECODE(EXC_STRVAL, EXC_ALLOC(o, t))
167 /* --- A union representing the type --- */
169 typedef union exc_exval
{
175 /*----- Predefined exceptions ---------------------------------------------*/
177 /* --- @EXC_NOMEM@ --- *
181 * Meaning: An attempt to allocate memory failed.
184 #define EXC_NOMEM EXC_ALLOCN(EXC_GLOBAL, 0u)
186 /* --- @EXC_ERRNO@ --- *
188 * Value: @int errno@ = the error raised
190 * Meaning: Some kind of OS error occurred.
193 #define EXC_ERRNO EXC_ALLOCI(EXC_GLOBAL, 1u)
195 /* --- @EXC_OSERROR@ --- *
197 * Value: @os_error *e@ = pointer to error block
199 * Meaning: For RISC OS programmers only: alternative way of propagating
203 #define EXC_OSERROR EXC_ALLOCP(EXC_GLOBAL, 1u)
205 /* --- @EXC_SIGNAL@ --- *
207 * Value: @int sig@ = signal number
209 * Meaning: Report the raising of a signal.
212 #define EXC_SIGNAL EXC_ALLOCI(EXC_GLOBAL, 2u)
214 /* --- @EXC_FAIL@ --- *
216 * Value: @const char *p@ = pointer to expanatory string
218 * Meaning: Miscellaneous error.
221 #define EXC_FAIL EXC_ALLOCS(EXC_GLOBAL, 0xFFu)
223 /*----- An exception handler block ----------------------------------------*/
225 /* --- Try to think of this as being opaque --- */
227 typedef struct __exc_hnd
{
228 struct __exc_hnd
*next
; /* Pointer to next record down */
229 exc_extype type
; /* Type of this exception */
230 exc_exval val
; /* Value of this exception */
231 jmp_buf buf
; /* Jump buffer when exceptions hit */
234 /*----- Global variables --------------------------------------------------*/
236 extern __exc_hnd
*__exc_list
; /* List of active handlers */
238 /*----- Macros ------------------------------------------------------------*/
240 /* --- References to current exception type and value --- */
242 #define exc_type (__exc_ec.type)
243 #define exc_val (__exc_ec.val)
244 #define exc_i (__exc_ec.val.i)
245 #define exc_p (__exc_ec.val.p)
246 #define exc_s (__exc_ec.val.s)
248 /* --- How it actually works --- *
250 * A `try' block is contained within a block which provides an exception
251 * handler buffer in automatic storage. This block is a loop, to allow
252 * `break' to escape from it. It adds the handler buffer to the top of a
253 * list, and does a `setjmp' to allow a return here following an exception.
254 * The `setjmp' returns zero for the `try' section, and nonzero if there's
255 * an exception to `catch'. It looks a little like this:
260 * if (!setjmp(h.buf)) {
261 * do <try code> while (0);
262 * remove_handler(&h);
267 * Everything else is ugly hacking to make things work.
270 /* --- Trying things which may cause exceptions --- */
273 volatile __exc_hnd __exc_ec; \
274 __exc_ec.next = __exc_list; \
275 __exc_list = (__exc_hnd *)&__exc_ec; \
276 if (!setjmp(*(jmp_buf *)&__exc_ec.buf /* very nasty! */ )) { do
278 #define EXIT_TRY do __exc_list = __exc_ec.next; while (0)
279 #define CATCH while (0); EXIT_TRY; } else
281 #define END_TRY } while (0)
283 /* --- Raising exceptions --- */
285 #define THROW __exc_throw
286 #define RETHROW __exc_rethrow(__exc_ec.type, __exc_ec.val)
288 /*----- Functions ---------------------------------------------------------*/
290 /* --- @exc_uncaught@ --- *
292 * Arguments: @void (*proc)(exc_extype type, exc_exval val) = new handler
294 * Returns: Pointer to the old handler value.
296 * Use: Sets the handler for uncaught exceptions.
299 typedef void (*exc__uncaught
)(exc_extype
/*type*/, exc_exval
/*val*/);
300 extern exc__uncaught
exc_uncaught(exc__uncaught
/*proc*/);
302 /* --- @__exc_throw@ --- *
304 * Arguments: @exc_extype type@ = type of exception to throw
308 * Use: NOT FOR USER CONSUMPTION. Reads an appropriate exception
309 * value and throws an exception.
312 extern void __exc_throw(exc_extype
/*type*/, ...);
314 /* --- @__exc_rethrow@ --- *
316 * Arguments: @exc_extype type@ = type of exception to throw
317 * @exc_exval val@ = value of exception to throw
321 * Use: NOT FOR USER CONSUMPTION. Does the donkey-work of raising
325 extern void __exc_rethrow(exc_extype
/*type*/, exc_exval
/*val*/);
327 /*----- That's all, folks -------------------------------------------------*/