Lichen

templates/cexcept.h

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