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