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. */