1: /* exception.h --- header implementing lightweight, but featured
2: exception handling.
4: Time-stamp: "2005-04-10 17:06:31 admp"
6: Copyright (C) 2003-5 Adomas P. <adomas.paltanavicius@gmail.com>
8: This program is free software; you can redistribute it and/or modify
9: it under the terms of the GNU General Public License as published by
10: the Free Software Foundation; either version 2 of the License, or
11: (at your option) any later version.
13: This program is distributed in the hope that it will be useful,
14: but WITHOUT ANY WARRANTY; without even the implied warranty of
15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: GNU General Public License for more details.
18: You should have received a copy of the GNU General Public License
19: along with this program; if not, write to the Free Software
20: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */
22: /* Summary.
24: To understand all this, you should be experienced in C, and also
25: know the setjmp interface. (Unixers do.)
27: Implemented:
28: 1. TRY --- a catcher block.
29: 2. EXCEPT, EXPECT, CATCH --- a handler block.
30: 3. THROW --- throws an exception.
31: 4. ON --- a handler.
33: IMPORTANT:
34: TRY without EXCEPT does not work properly. This is impossible
35: to fix, at least I don't see the way. Anyway, if you don't
36: want to break the whole system, I see no other need for
37: such a construct.
39: Syntax:
40: 1. TRY
41: The syntax is:
42: try { statements; } except { handler blocks; }
44: 2. EXCEPT, EXPECT, CATCH
45: The syntax is:
46: except { handler blocks; }
48: 3. THROW
49: The syntax is:
50: THROW (params)
51: PARAMS depend on your local implementation. By default, it is a
52: number (int).
53:
54: 4. ON
55: The syntax is:
56: on (params) { statements; }
57: PARAMS are the same as for THROW.
59: Supports:
60: 1. Nested catcher blocks.
61: 2. Raising an exception outside catcher block (this results in an
62: unhandled exception.)
63: 3. Raising an exception in function called from catcher block.
64: 4. Different types for exception structures.
65: 5. Reporting file and line where exception was thrown/handled. */
67: /* Search for TODO and FIXME tags if you want to enchance this.
68: (This would be nice.) */
70: /* TODO: since customized exceptions usually allocate memory
71: dynamically, it would be nice to have support for freeing
72: function, which would be called after it's not needed
73: anymore: __EXC_FREE. I. e. after exception is handled. */
75: #if !defined __EXCEPTION_H__
76: #define __EXCEPTION_H__
78: #include <setjmp.h>
79: #include <stdlib.h>
80: #include <stdio.h>
81: #include <string.h>
83: /* The default prefix is `__exc' for functions and `__EXC' for
84: macros. Underscores mean you shouldn't touch 'em! */
88: /* C syntax hacks.
90: Oh, god, I felt like an inventor after writing these.
91: Clearly, code
93: for (start (), J = 1; J; end (), J = 0)
94: code ();
96: Does this:
97: 1) Executes START
98: 2) Executes CODE
99: 3) Executes END
100: 4) ...And terminates.
102: It also works if nested (think why yourself.)
103: */
105: /* Execute START, then block after the macro, and finally END. */
107: #define __EXC_BLOCK(start, end) \
108: for (start, __exc_block_pass = 1; \
109: __exc_block_pass; \
110: end, __exc_block_pass = 0)
112: /* Likewise, but START block is empty. */
114: #define __EXC_END(end) \
115: for (__exc_block_pass = 1; \
116: __exc_block_pass; \
117: end, __exc_block_pass = 0)
121: /* For function name. GCC includes things which expand to
122: the name of current function's name. */
124: #if (!defined (__GNUC__) || __GNUC__ < 2 || \
125: __GNUC_MINOR__ < (defined (__cplusplus) ? 6 : 4))
126: /* Otherwise stick to unknown. */
127: # define __EXC_FUNCTION (char *) 0
128: #else
129: # define __EXC_FUNCTION __PRETTY_FUNCTION__
130: #endif
134: /* The exception value. */
136: /* You'll want to make local changes to these. For example, to use
137: your own exception structure. */
139: /* Exception is by default an int. Anyway, it can be anything from
140: string to some structure.
142: Whatever the implementation you choose, type name should be
143: defined as __EXC_TYPE. The THROW (and ON) macro accepts as
144: many arguments, s it is given, so your function may use all
145: the power of argument passing. Define your function's name
146: as __EXC_MAKE. Exceptions are compared in ON macro. You
147: should define comparing function as __EXC_EQ.
149: For example, if you'd like to use strings in place of numbers,
150: use this snippet:
152: 1) #define __EXC_TYPE char *
153: 2) #define __EXC_EQ(s1, s2) (strcasecmp (s1, s2) == 0)
154: 3) #define __EXC_PRINT(e, stream) \
155: fprintf (stream, "%s", e)
157: */
159: #ifndef __EXC_TYPE
160: # define __EXC_TYPE int
162: /* Include the default __EXC_PRINT. */
163: # define __EXC_TYPE_DEFAULT
164: #endif
166: #ifndef __EXC_MAKE
167: # define __EXC_MAKE(code...) code
168: #endif
170: #ifndef __EXC_ON
171: # define __EXC_ON __EXC_MAKE
172: #endif
174: #ifndef __EXC_EQ
175: # define __EXC_EQ(c1, c2) ((c1) == (c2))
176: #endif
179: /* Optional exception printer. This is used for debugging purposes
180: only. Define yourself's one as __EXC_PRINT. Arguments are
181: exception of type __EXC_TYPE and stream to print to. */
183: #if !defined (__EXC_PRINT) && defined (__EXC_TYPE_DEFAULT)
184: # define __EXC_PRINT(e, stream) \
185: fprintf (stream, "%d", e)
186: #endif
190: /* All variables are declared volatile to force non-optimization.
191: They should also be declared as thread-local. */
193: /* This counter is used by __EXC_BLOCK. It works well even if nested. */
194: extern volatile int __exc_block_pass;
196: /* Flag to be set by ON? */
197: extern volatile int __exc_handled;
199: /* For indexing every call to TRY. */
200: extern volatile unsigned __exc_tries;
202: /* These identify the thrown exception. File, function, line and
203: the exception itself. */
204: extern char *__exc_file;
205: extern char *__exc_function;
206: extern unsigned __exc_line;
207: extern volatile __EXC_TYPE __exc_code;
209: /* Stack is actually a linked list of catcher cells. */
210: struct __exc_stack
211: {
212: unsigned num;
213: jmp_buf j;
214: struct __exc_stack *prev;
215: };
217: /* This is the global stack of catchers. */
218: extern struct __exc_stack *__exc_global;
222: /* Debugging of exceptions. Nothing interesting for you. (Just for me.)
223: Anyway, it generates many (really) messages telling what is going
224: to happen. I order to work with it successfully, you should define
225: __EXC_PRINT (see above.)
226: */
227: #ifdef __EXC_DEBUG
228: # include <stdarg.h>
229: # ifndef __EXC_STREAM
230: /* I often redirect debugging information to a file. */
231: # define __EXC_STREAM stdout
232: # endif
234: /* Prints error message. */
235: extern void __exc_debug(char *, ...);
237: /* For printing __exc_global. */
238: extern void __exc_print_global(void);
240: #else
241: # define __exc_debug(args...)
242: # define __exc_print_global()
243: #endif
247: /* Prints information about exception. Called in debug mode, or when
248: no handler is found. */
249: extern void __exc_print (FILE *, char *, char *,
250: unsigned, __EXC_TYPE);
252: /* Pop exception from stack, putting into J (if nonzero). If stack is
253: empty, print error message and exit. Used in EXCEPT. */
254: extern void __exc_pop (jmp_buf *);
258: /* Push J onto the stack, with RETURNED as value from SETJMP. Return
259: nonzero, if RETURNED is 0. If RETURNED is nonzero, returns 0.
260: Used in TRY. */
261: extern int __exc_push (jmp_buf *, int);
265: /* Throw an exception in FILE at LINE, with code CODE. Used in THROW. */
266: extern __attribute__((noreturn)) void __exc_throw (char *, char *, unsigned, __EXC_TYPE);
268: /* Throw it in upper level of catcher blocks. */
269: extern void __exc_rethrow ();
273: /* What a f... Somewhy I can't get GCC's __attribute__ working here
274: to tell that FILE and LINE are unused in non-debuging mode. */
276: /* TODO: define __attribute__ (foo) to do nothing if this is not GCC.
277: (Check if GNUC is predefined.) */
279: extern int __exc_on (char *, char *, unsigned, __EXC_TYPE);
283: /* Start catching. */
285: /* Obviously, there is no way to check if appropriate EXCEPT exists.
286: Its non-exsistence won't do segfault etc.; program will simply do
287: the thing after TRY, without any error handling. Raising from
288: there works. */
290: #define try \
291: if (({jmp_buf __exc_j; \
292: int __exc_ret; \
293: __exc_ret = setjmp (__exc_j); \
294: __exc_push (&__exc_j, __exc_ret);})) \
295: __EXC_END(__exc_pop (0))
297: #define throw(code...) \
298: __exc_throw (__FILE__, __EXC_FUNCTION, \
299: __LINE__, __EXC_MAKE (code))
301: /* THROW in EXCEPT block won't go into itself, because corresping item
302: from __EXC_GLOBAL was already popped. */
304: #define except \
305: else \
306: __EXC_BLOCK (__exc_handled = 0, \
307: ({ if (__exc_handled == 0) \
308: __exc_rethrow (); }))
310: /* EXPECT is an alias for EXCEPT. */
312: #define expect except
314: /* CATCH is an alias for EXCEPT. */
316: #define catch except
318: /* Try to handle an exception. */
320: #define on(code...) \
321: if (__exc_on (__FILE__, __EXC_FUNCTION, \
322: __LINE__, __EXC_ON (code)))
324: #endif
325: /* End. */