Home | History | Annotate | Download | only in visupng
      1 /*===
      2 cexcept.h 2.0.1 (2008-Jul-19-Sat)
      3 http://www.nicemice.net/cexcept/
      4 Adam M. Costello
      5 http://www.nicemice.net/amc/
      6 
      7 An interface for exception-handling in ANSI C (C89 and subsequent ISO
      8 standards), developed jointly with Cosmin Truta.
      9 
     10     Copyright (c) 2000-2008 Adam M. Costello and Cosmin Truta.
     11     This software may be modified only if its author and version
     12     information is updated accurately, and may be redistributed
     13     only if accompanied by this unaltered notice.  Subject to those
     14     restrictions, permission is granted to anyone to do anything
     15     with this software.  The copyright holders make no guarantees
     16     regarding this software, and are not responsible for any damage
     17     resulting from its use.
     18 
     19 The cexcept interface is not compatible with and cannot interact
     20 with system exceptions (like division by zero or memory segmentation
     21 violation), compiler-generated exceptions (like C++ exceptions), or
     22 other exception-handling interfaces.
     23 
     24 When using this interface across multiple .c files, do not include
     25 this header file directly.  Instead, create a wrapper header file that
     26 includes this header file and then invokes the define_exception_type
     27 macro (see below).  The .c files should then include that header file.
     28 
     29 The interface consists of one type, one well-known name, and six macros.
     30 
     31 
     32 define_exception_type(type_name);
     33 
     34     This macro is used like an external declaration.  It specifies
     35     the type of object that gets copied from the exception thrower to
     36     the exception catcher.  The type_name can be any type that can be
     37     assigned to, that is, a non-constant arithmetic type, struct, union,
     38     or pointer.  Examples:
     39 
     40         define_exception_type(int);
     41 
     42         enum exception { out_of_memory, bad_arguments, disk_full };
     43         define_exception_type(enum exception);
     44 
     45         struct exception { int code; const char *msg; };
     46         define_exception_type(struct exception);
     47 
     48     Because throwing an exception causes the object to be copied (not
     49     just once, but twice), programmers may wish to consider size when
     50     choosing the exception type.
     51 
     52 
     53 struct exception_context;
     54 
     55     This type may be used after the define_exception_type() macro has
     56     been invoked.  A struct exception_context must be known to both
     57     the thrower and the catcher.  It is expected that there be one
     58     context for each thread that uses exceptions.  It would certainly
     59     be dangerous for multiple threads to access the same context.
     60     One thread can use multiple contexts, but that is likely to be
     61     confusing and not typically useful.  The application can allocate
     62     this structure in any way it pleases--automatic, static, or dynamic.
     63     The application programmer should pretend not to know the structure
     64     members, which are subject to change.
     65 
     66 
     67 struct exception_context *the_exception_context;
     68 
     69     The Try/Catch and Throw statements (described below) implicitly
     70     refer to a context, using the name the_exception_context.  It is
     71     the application's responsibility to make sure that this name yields
     72     the address of a mutable (non-constant) struct exception_context
     73     wherever those statements are used.  Subject to that constraint, the
     74     application may declare a variable of this name anywhere it likes
     75     (inside a function, in a parameter list, or externally), and may
     76     use whatever storage class specifiers (static, extern, etc) or type
     77     qualifiers (const, volatile, etc) it likes.  Examples:
     78 
     79         static struct exception_context
     80           * const the_exception_context = &foo;
     81 
     82         { struct exception_context *the_exception_context = bar; ... }
     83 
     84         int blah(struct exception_context *the_exception_context, ...);
     85 
     86         extern struct exception_context the_exception_context[1];
     87 
     88     The last example illustrates a trick that avoids creating a pointer
     89     object separate from the structure object.
     90 
     91     The name could even be a macro, for example:
     92 
     93         struct exception_context ec_array[numthreads];
     94         #define the_exception_context (ec_array + thread_id)
     95 
     96     Be aware that the_exception_context is used several times by the
     97     Try/Catch/Throw macros, so it shouldn't be expensive or have side
     98     effects.  The expansion must be a drop-in replacement for an
     99     identifier, so it's safest to put parentheses around it.
    100 
    101 
    102 void init_exception_context(struct exception_context *ec);
    103 
    104     For context structures allocated statically (by an external
    105     definition or using the "static" keyword), the implicit
    106     initialization to all zeros is sufficient, but contexts allocated
    107     by other means must be initialized using this macro before they
    108     are used by a Try/Catch statement.  It does no harm to initialize
    109     a context more than once (by using this macro on a statically
    110     allocated context, or using this macro twice on the same context),
    111     but a context must not be re-initialized after it has been used by a
    112     Try/Catch statement.
    113 
    114 
    115 Try statement
    116 Catch (expression) statement
    117 
    118     The Try/Catch/Throw macros are capitalized in order to avoid
    119     confusion with the C++ keywords, which have subtly different
    120     semantics.
    121 
    122     A Try/Catch statement has a syntax similar to an if/else statement,
    123     except that the parenthesized expression goes after the second
    124     keyword rather than the first.  As with if/else, there are two
    125     clauses, each of which may be a simple statement ending with a
    126     semicolon or a brace-enclosed compound statement.  But whereas
    127     the else clause is optional, the Catch clause is required.  The
    128     expression must be a modifiable lvalue (something capable of being
    129     assigned to) of the same type (disregarding type qualifiers) that
    130     was passed to define_exception_type().
    131 
    132     If a Throw that uses the same exception context as the Try/Catch is
    133     executed within the Try clause (typically within a function called
    134     by the Try clause), and the exception is not caught by a nested
    135     Try/Catch statement, then a copy of the exception will be assigned
    136     to the expression, and control will jump to the Catch clause.  If no
    137     such Throw is executed, then the assignment is not performed, and
    138     the Catch clause is not executed.
    139 
    140     The expression is not evaluated unless and until the exception is
    141     caught, which is significant if it has side effects, for example:
    142 
    143         Try foo();
    144         Catch (p[++i].e) { ... }
    145 
    146     IMPORTANT: Jumping into or out of a Try clause (for example via
    147     return, break, continue, goto, longjmp) is forbidden--the compiler
    148     will not complain, but bad things will happen at run-time.  Jumping
    149     into or out of a Catch clause is okay, and so is jumping around
    150     inside a Try clause.  In many cases where one is tempted to return
    151     from a Try clause, it will suffice to use Throw, and then return
    152     from the Catch clause.  Another option is to set a flag variable and
    153     use goto to jump to the end of the Try clause, then check the flag
    154     after the Try/Catch statement.
    155 
    156     IMPORTANT: The values of any non-volatile automatic variables
    157     changed within the Try clause are undefined after an exception is
    158     caught.  Therefore, variables modified inside the Try block whose
    159     values are needed later outside the Try block must either use static
    160     storage or be declared with the "volatile" type qualifier.
    161 
    162 
    163 Throw expression;
    164 
    165     A Throw statement is very much like a return statement, except that
    166     the expression is required.  Whereas return jumps back to the place
    167     where the current function was called, Throw jumps back to the Catch
    168     clause of the innermost enclosing Try clause.  The expression must
    169     be compatible with the type passed to define_exception_type().  The
    170     exception must be caught, otherwise the program may crash.
    171 
    172     Slight limitation:  If the expression is a comma-expression, it must
    173     be enclosed in parentheses.
    174 
    175 
    176 Try statement
    177 Catch_anonymous statement
    178 
    179     When the value of the exception is not needed, a Try/Catch statement
    180     can use Catch_anonymous instead of Catch (expression).
    181 
    182 
    183 Everything below this point is for the benefit of the compiler.  The
    184 application programmer should pretend not to know any of it, because it
    185 is subject to change.
    186 
    187 ===*/
    188 
    189 
    190 #ifndef CEXCEPT_H
    191 #define CEXCEPT_H
    192 
    193 
    194 #include <setjmp.h>
    195 
    196 #define define_exception_type(etype) \
    197 struct exception_context { \
    198   jmp_buf *penv; \
    199   int caught; \
    200   volatile struct { etype etmp; } v; \
    201 }
    202 
    203 /* etmp must be volatile because the application might use automatic */
    204 /* storage for the_exception_context, and etmp is modified between   */
    205 /* the calls to setjmp() and longjmp().  A wrapper struct is used to */
    206 /* avoid warnings about a duplicate volatile qualifier in case etype */
    207 /* already includes it.                                              */
    208 
    209 #define init_exception_context(ec) ((void)((ec)->penv = 0))
    210 
    211 #define Try \
    212   { \
    213     jmp_buf *exception__prev, exception__env; \
    214     exception__prev = the_exception_context->penv; \
    215     the_exception_context->penv = &exception__env; \
    216     if (setjmp(exception__env) == 0) { \
    217       do
    218 
    219 #define exception__catch(action) \
    220       while (the_exception_context->caught = 0, \
    221              the_exception_context->caught); \
    222     } \
    223     else { \
    224       the_exception_context->caught = 1; \
    225     } \
    226     the_exception_context->penv = exception__prev; \
    227   } \
    228   if (!the_exception_context->caught || action) { } \
    229   else
    230 
    231 #define Catch(e) exception__catch(((e) = the_exception_context->v.etmp, 0))
    232 #define Catch_anonymous exception__catch(0)
    233 
    234 /* Try ends with do, and Catch begins with while(0) and ends with     */
    235 /* else, to ensure that Try/Catch syntax is similar to if/else        */
    236 /* syntax.                                                            */
    237 /*                                                                    */
    238 /* The 0 in while(0) is expressed as x=0,x in order to appease        */
    239 /* compilers that warn about constant expressions inside while().     */
    240 /* Most compilers should still recognize that the condition is always */
    241 /* false and avoid generating code for it.                            */
    242 
    243 #define Throw \
    244   for (;; longjmp(*the_exception_context->penv, 1)) \
    245     the_exception_context->v.etmp =
    246 
    247 
    248 #endif /* CEXCEPT_H */
    249