home *** CD-ROM | disk | FTP | other *** search
/ Acorn User 10 / AU_CD10.iso / Updates / DigitalCD / !DigitalCD / Copy / PowerBars / h / Exception < prev    next >
Text File  |  1998-11-30  |  9KB  |  253 lines

  1. /*** exception.h ***/
  2. /* C exception handling system
  3.  * (c) Paul Field
  4.  * v1.00 - 23/8/1995
  5.  * v1.01 -  1/9/1995 : Added some more debugging code
  6.  * v1.02 - 29/9/1995 : Added backtrace output on exception for debugging purposes
  7.  * v1.03 -  9/5/1996 : Catches SIGABRT (which stops assert() exiting the program)
  8.  *
  9.  * based on code by Martin Ebourne and Jos Horsmeier
  10.  * (see also David A. Spuler "C++ and C Debugging, Testing and Reliability",
  11.  *  Prentice Hall, ISBN 0-13-308172-9)
  12.  *
  13.  * N.B. This module contains debugging code which will catch some of the
  14.  * mistakes you might make with this module. To disable this code (for the
  15.  * final version of your program), compile with the macro NDEBUG defined.
  16.  *
  17.  * This implementation is intended to:
  18.  *  1) keep things simple:
  19.  *     a) macros are necessary for the implementation but this
  20.  *        version only uses 3; it doesn't try to do clever things in the
  21.  *        catch block (e.g. the EXCEPTION() and ALLEXCEPT macros in
  22.  *        Horsmeier's code)
  23.  *     b) error reporting is left to other program modules - this gives the user
  24.  *        more control over error handling and reporting
  25.  *  2) keep 'normal' code fast: although in most cases the effect will be
  26.  *     negligable, this implementation tries to make the code executed by
  27.  *     an un-exceptional use of the 'try' block as fast as possible.
  28.  *  3) Provide support for three types of errors:
  29.  *       os_errors    (either thrown internally or by OSLib)
  30.  *       signals      (SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM)
  31.  *       user-defined (a user-defined code; useful when returning a message
  32.  *                     is not appropriate)
  33.  *
  34.  * How to use this module
  35.  * ~~~~~~~~~~~~~~~~~~~~~~
  36.  * Before doing anything else, call exception_initialise()
  37.  *
  38.  * To handle exceptions you use a try...catch...catch_end construct:
  39.  *
  40.  *   (1) some code
  41.  *
  42.  *   try
  43.  *    { (2) try block (DO NOT 'return' or 'break' out of this block
  44.  *                     [or, less likely, 'goto', 'longjmp' or 'continue' out of it])
  45.  *    }
  46.  *   catch
  47.  *    { (3) catch block
  48.  *    }
  49.  *   catch_end
  50.  *
  51.  *   (4) more code
  52.  *
  53.  * Normally execution would go 1,2,4. However, if an exception occurs while 2
  54.  * is being executed (the exception can occur in functions called in 2) then
  55.  * the flow of control will be 1,some of 2,3,4. In other words the catch
  56.  * block contains error-handling code that will be called whenever an error
  57.  * (in the form of an exception) occurs in the try block.
  58.  *
  59.  * You can have many try...catch...catch_end constructs in your program and they
  60.  * can be nested (i.e. appear inside another try or catch block). Because of
  61.  * this, a common thing to do in a catch block is to partly handle the error
  62.  * (e.g. free memory, close files) and then 'throw' the exception so that
  63.  * a 'higher-up' catch block will catch the exception and act on it.
  64.  *
  65.  * Generating exceptions
  66.  * ~~~~~~~~~~~~~~~~~~~~~
  67.  * Exceptions can be generated by:
  68.  *  1) calling the throw() routines (see below)
  69.  *  2) calling a non-X SWI (e.g. using OSLib)
  70.  *  3) doing something which causes a signal
  71.  *
  72.  * Handling exceptions (the catch block)
  73.  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  74.  * Often a catch block doesn't care what the exception was: it simply tidies
  75.  * up and then uses throw() to pass the exception on to a 'higher-up' catch
  76.  * block. However, many catch blocks need to know the nature of the exception;
  77.  * perhaps so they can generate an error message. Such a block can use
  78.  * exception_current(), perhaps like this:
  79.  *
  80.  * catch
  81.  *  { const exception *e = exception_current();
  82.  *    switch(e->type)
  83.  *     { case exception_signal: signal_report(e->error.signal_id); break;
  84.  *       case exception_os:     os_report(e->error.os);            break;
  85.  *       case exception_user:   user_report(e->error.user_id);     break;
  86.  *     }
  87.  *  }
  88.  * catch_end
  89.  *
  90.  * This example is for exposition only. A real program is likely to have
  91.  * a general-purpose exception reporting function.
  92.  *
  93.  * If an exception occurs outside of a catch block then exit() will be called
  94.  * (so any atexit()-registered functions will be called - perhaps to try to
  95.  * emergency-save data). In this case, the error will go unreported so it's
  96.  * usually better to use a try...catch...catch_end around all the code in main()
  97.  *
  98.  *
  99.  * Things to watch out for
  100.  * ~~~~~~~~~~~~~~~~~~~~~~~
  101.  * Because try...catch...catch_end is not part of the language, the compiler
  102.  * cannot check the syntax. However, if you misuse it you will generally
  103.  * get some weird errors so, if an error doesn't make sense, check the
  104.  * try...catch...catch_end structure.
  105.  *
  106.  *
  107.  * Unless they are declared as 'volatile', automatic variables that are
  108.  * modified within the try block will have an undefined value after
  109.  * the try block if an exception occurs i.e.
  110.  *
  111.  * { int i = 1;               // automatic variable
  112.  *   try
  113.  *    { i = 2;                 // modified in try block
  114.  *      generate_exception();
  115.  *    }
  116.  *   catch
  117.  *    { // i is undefined here
  118.  *    }
  119.  *   catch_end
  120.  *   // i is undefined here
  121.  *
  122.  * Debugging
  123.  * ~~~~~~~~~
  124.  * If this code is compiled in debugging mode (i.e. the macro NDEBUG is *not* defined)
  125.  * then, on exception, the exception is output to stderr along with a stack dump.
  126.  * The exception is then processed as usual.
  127.  */
  128.  
  129.  
  130.  
  131. #ifndef _exception_h
  132. #define _exception_h
  133.  
  134. #include <setjmp.h>
  135. #include <assert.h>
  136.  
  137. #include "kernel.h"
  138.  
  139. typedef enum
  140. {
  141.     exception_signal,
  142.     exception_os,
  143.     exception_user
  144. } exception_type;
  145.  
  146. typedef struct exception_str
  147. {
  148.     exception_type    type;
  149.     union
  150.     {
  151.         unsigned        signal_id;
  152.         unsigned        user_id;
  153.         const _kernel_oserror*    os;
  154.     } error;
  155.     const char*    file;
  156.     int        line;
  157. } exception;
  158.  
  159. void exception_initialise(void);
  160.  /* Sets up the exception system.
  161.   * Call before using any exception routines.
  162.   */
  163.  
  164. const exception* exception_current(void);
  165.  /* Returns a description of the current exception.
  166.   * The value returned is undefined if this is used outside of a 'catch' block
  167.   */
  168.  
  169. void __throw_user(unsigned id, char*, int);
  170.  /* Throws an exception of type exception_user.
  171.   */
  172. #define throw_user(x) __throw_user(x, __FILE__, __LINE__)
  173.  
  174. void __throw_os(const _kernel_oserror* err, char*, int);
  175.  /* Throws an exception of type exception_os.
  176.   * 'err' should not be stored on the stack.
  177.   */
  178. #define throw_os(x) __throw_os(x, __FILE__, __LINE__)
  179.  
  180. void __throw_last_os_error(char*, int);
  181.  /* Throws an exception of type exception_os.
  182.   */
  183. #define throw_last_os_error() __throw_last_os_error( __FILE__, __LINE__)
  184.  
  185. #pragma -v1 /* hint to the compiler to check f/s/printf format */
  186. void __throw_string(char*, int, const char* pformat, ...);
  187. #pragma -v0 /* return to default */
  188.  /* Throws an exception of type exception_os turning
  189.   * string into an os_error to do so.
  190.   */
  191. #define throw_string(x) __throw_string(__FILE__, __LINE__, x)
  192.  
  193. void throw(void);
  194.  /* Throws the current exception.
  195.   * (Only makes sense to call this within a 'catch' block)
  196.   */
  197.  
  198. /*********************************************
  199.  *   ABANDON HOPE ALL YE WHO ENTER HERE :-)  *
  200.  *                                           *
  201.  * Beyond this point are implementation      *
  202.  * details which you do not need to be aware *
  203.  * of and should not rely on in any way      *
  204.  * (because they could change in a future    *
  205.  * release)                                  *
  206.  *********************************************/
  207.  
  208. /* The exception system is currently implemented
  209.  * as a linked-list of jumpbufs on the stack.
  210.  */
  211. typedef struct _exception_block_str
  212. {
  213.     struct _exception_block_str*    previous;
  214.     jmp_buf                jmpbuf;
  215. } _exception_block;
  216.  
  217. extern _exception_block* _exception_list;
  218.  
  219. /* A macro that detects list corruption
  220.  * (probably caused by breaking or returning from a try block)
  221.  */
  222. #define escape_from_nested_try_using_return_or_break() (_exception_list != &_exception_current)
  223.  
  224. /* Basically, 'try' merely links a new _exception_block into the linked list
  225.  * and then sets up a jmpbuf so that 'throw' can use longjmp to get at the
  226.  * catch block.
  227.  *
  228.  * The instructions that link in the block could have been put in a function:
  229.  * this would save 1 or 2 ARM instructions (depending on how the compiler optimises
  230.  * the code) but would incur a function call overhead.
  231.  */
  232. #define try\
  233.  { _exception_block _exception_current;\
  234.    _exception_current.previous = _exception_list;\
  235.    _exception_list = &_exception_current;\
  236.    if (!setjmp(_exception_current.jmpbuf))\
  237.     {
  238.  
  239. #define catch\
  240.       assert(!escape_from_nested_try_using_return_or_break());\
  241.       _exception_list = _exception_current.previous;\
  242.     }\
  243.    else\
  244.     { /* Don't need to remove block from list - throw_xxx() will have\
  245.        * done this already\
  246.        */
  247.  
  248. #define catch_end\
  249.     }\
  250.  }
  251.  
  252. #endif
  253.