mirror of
				https://git.code.sf.net/p/libpng/code.git
				synced 2025-07-10 18:04:09 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			249 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*===
 | |
| cexcept.h 2.0.1 (2008-Jul-19-Sat)
 | |
| http://www.nicemice.net/cexcept/
 | |
| Adam M. Costello
 | |
| http://www.nicemice.net/amc/
 | |
| 
 | |
| An interface for exception-handling in ANSI C (C89 and subsequent ISO
 | |
| standards), developed jointly with Cosmin Truta.
 | |
| 
 | |
|     Copyright (c) 2000-2008 Adam M. Costello and Cosmin Truta.
 | |
|     This software may be modified only if its author and version
 | |
|     information is updated accurately, and may be redistributed
 | |
|     only if accompanied by this unaltered notice.  Subject to those
 | |
|     restrictions, permission is granted to anyone to do anything
 | |
|     with this software.  The copyright holders make no guarantees
 | |
|     regarding this software, and are not responsible for any damage
 | |
|     resulting from its use.
 | |
| 
 | |
| The cexcept interface is not compatible with and cannot interact
 | |
| with system exceptions (like division by zero or memory segmentation
 | |
| violation), compiler-generated exceptions (like C++ exceptions), or
 | |
| other exception-handling interfaces.
 | |
| 
 | |
| When using this interface across multiple .c files, do not include
 | |
| this header file directly.  Instead, create a wrapper header file that
 | |
| includes this header file and then invokes the define_exception_type
 | |
| macro (see below).  The .c files should then include that header file.
 | |
| 
 | |
| The interface consists of one type, one well-known name, and six macros.
 | |
| 
 | |
| 
 | |
| define_exception_type(type_name);
 | |
| 
 | |
|     This macro is used like an external declaration.  It specifies
 | |
|     the type of object that gets copied from the exception thrower to
 | |
|     the exception catcher.  The type_name can be any type that can be
 | |
|     assigned to, that is, a non-constant arithmetic type, struct, union,
 | |
|     or pointer.  Examples:
 | |
| 
 | |
|         define_exception_type(int);
 | |
| 
 | |
|         enum exception { out_of_memory, bad_arguments, disk_full };
 | |
|         define_exception_type(enum exception);
 | |
| 
 | |
|         struct exception { int code; const char *msg; };
 | |
|         define_exception_type(struct exception);
 | |
| 
 | |
|     Because throwing an exception causes the object to be copied (not
 | |
|     just once, but twice), programmers may wish to consider size when
 | |
|     choosing the exception type.
 | |
| 
 | |
| 
 | |
| struct exception_context;
 | |
| 
 | |
|     This type may be used after the define_exception_type() macro has
 | |
|     been invoked.  A struct exception_context must be known to both
 | |
|     the thrower and the catcher.  It is expected that there be one
 | |
|     context for each thread that uses exceptions.  It would certainly
 | |
|     be dangerous for multiple threads to access the same context.
 | |
|     One thread can use multiple contexts, but that is likely to be
 | |
|     confusing and not typically useful.  The application can allocate
 | |
|     this structure in any way it pleases--automatic, static, or dynamic.
 | |
|     The application programmer should pretend not to know the structure
 | |
|     members, which are subject to change.
 | |
| 
 | |
| 
 | |
| struct exception_context *the_exception_context;
 | |
| 
 | |
|     The Try/Catch and Throw statements (described below) implicitly
 | |
|     refer to a context, using the name the_exception_context.  It is
 | |
|     the application's responsibility to make sure that this name yields
 | |
|     the address of a mutable (non-constant) struct exception_context
 | |
|     wherever those statements are used.  Subject to that constraint, the
 | |
|     application may declare a variable of this name anywhere it likes
 | |
|     (inside a function, in a parameter list, or externally), and may
 | |
|     use whatever storage class specifiers (static, extern, etc) or type
 | |
|     qualifiers (const, volatile, etc) it likes.  Examples:
 | |
| 
 | |
|         static struct exception_context
 | |
|           * const the_exception_context = &foo;
 | |
| 
 | |
|         { struct exception_context *the_exception_context = bar; ... }
 | |
| 
 | |
|         int blah(struct exception_context *the_exception_context, ...);
 | |
| 
 | |
|         extern struct exception_context the_exception_context[1];
 | |
| 
 | |
|     The last example illustrates a trick that avoids creating a pointer
 | |
|     object separate from the structure object.
 | |
| 
 | |
|     The name could even be a macro, for example:
 | |
| 
 | |
|         struct exception_context ec_array[numthreads];
 | |
|         #define the_exception_context (ec_array + thread_id)
 | |
| 
 | |
|     Be aware that the_exception_context is used several times by the
 | |
|     Try/Catch/Throw macros, so it shouldn't be expensive or have side
 | |
|     effects.  The expansion must be a drop-in replacement for an
 | |
|     identifier, so it's safest to put parentheses around it.
 | |
| 
 | |
| 
 | |
| void init_exception_context(struct exception_context *ec);
 | |
| 
 | |
|     For context structures allocated statically (by an external
 | |
|     definition or using the "static" keyword), the implicit
 | |
|     initialization to all zeros is sufficient, but contexts allocated
 | |
|     by other means must be initialized using this macro before they
 | |
|     are used by a Try/Catch statement.  It does no harm to initialize
 | |
|     a context more than once (by using this macro on a statically
 | |
|     allocated context, or using this macro twice on the same context),
 | |
|     but a context must not be re-initialized after it has been used by a
 | |
|     Try/Catch statement.
 | |
| 
 | |
| 
 | |
| Try statement
 | |
| Catch (expression) statement
 | |
| 
 | |
|     The Try/Catch/Throw macros are capitalized in order to avoid
 | |
|     confusion with the C++ keywords, which have subtly different
 | |
|     semantics.
 | |
| 
 | |
|     A Try/Catch statement has a syntax similar to an if/else statement,
 | |
|     except that the parenthesized expression goes after the second
 | |
|     keyword rather than the first.  As with if/else, there are two
 | |
|     clauses, each of which may be a simple statement ending with a
 | |
|     semicolon or a brace-enclosed compound statement.  But whereas
 | |
|     the else clause is optional, the Catch clause is required.  The
 | |
|     expression must be a modifiable lvalue (something capable of being
 | |
|     assigned to) of the same type (disregarding type qualifiers) that
 | |
|     was passed to define_exception_type().
 | |
| 
 | |
|     If a Throw that uses the same exception context as the Try/Catch is
 | |
|     executed within the Try clause (typically within a function called
 | |
|     by the Try clause), and the exception is not caught by a nested
 | |
|     Try/Catch statement, then a copy of the exception will be assigned
 | |
|     to the expression, and control will jump to the Catch clause.  If no
 | |
|     such Throw is executed, then the assignment is not performed, and
 | |
|     the Catch clause is not executed.
 | |
| 
 | |
|     The expression is not evaluated unless and until the exception is
 | |
|     caught, which is significant if it has side effects, for example:
 | |
| 
 | |
|         Try foo();
 | |
|         Catch (p[++i].e) { ... }
 | |
| 
 | |
|     IMPORTANT: Jumping into or out of a Try clause (for example via
 | |
|     return, break, continue, goto, longjmp) is forbidden--the compiler
 | |
|     will not complain, but bad things will happen at run-time.  Jumping
 | |
|     into or out of a Catch clause is okay, and so is jumping around
 | |
|     inside a Try clause.  In many cases where one is tempted to return
 | |
|     from a Try clause, it will suffice to use Throw, and then return
 | |
|     from the Catch clause.  Another option is to set a flag variable and
 | |
|     use goto to jump to the end of the Try clause, then check the flag
 | |
|     after the Try/Catch statement.
 | |
| 
 | |
|     IMPORTANT: The values of any non-volatile automatic variables
 | |
|     changed within the Try clause are undefined after an exception is
 | |
|     caught.  Therefore, variables modified inside the Try block whose
 | |
|     values are needed later outside the Try block must either use static
 | |
|     storage or be declared with the "volatile" type qualifier.
 | |
| 
 | |
| 
 | |
| Throw expression;
 | |
| 
 | |
|     A Throw statement is very much like a return statement, except that
 | |
|     the expression is required.  Whereas return jumps back to the place
 | |
|     where the current function was called, Throw jumps back to the Catch
 | |
|     clause of the innermost enclosing Try clause.  The expression must
 | |
|     be compatible with the type passed to define_exception_type().  The
 | |
|     exception must be caught, otherwise the program may crash.
 | |
| 
 | |
|     Slight limitation:  If the expression is a comma-expression, it must
 | |
|     be enclosed in parentheses.
 | |
| 
 | |
| 
 | |
| Try statement
 | |
| Catch_anonymous statement
 | |
| 
 | |
|     When the value of the exception is not needed, a Try/Catch statement
 | |
|     can use Catch_anonymous instead of Catch (expression).
 | |
| 
 | |
| 
 | |
| Everything below this point is for the benefit of the compiler.  The
 | |
| application programmer should pretend not to know any of it, because it
 | |
| is subject to change.
 | |
| 
 | |
| ===*/
 | |
| 
 | |
| 
 | |
| #ifndef CEXCEPT_H
 | |
| #define CEXCEPT_H
 | |
| 
 | |
| 
 | |
| #include <setjmp.h>
 | |
| 
 | |
| #define define_exception_type(etype) \
 | |
| struct exception_context { \
 | |
|   jmp_buf *penv; \
 | |
|   int caught; \
 | |
|   volatile struct { etype etmp; } v; \
 | |
| }
 | |
| 
 | |
| /* etmp must be volatile because the application might use automatic */
 | |
| /* storage for the_exception_context, and etmp is modified between   */
 | |
| /* the calls to setjmp() and longjmp().  A wrapper struct is used to */
 | |
| /* avoid warnings about a duplicate volatile qualifier in case etype */
 | |
| /* already includes it.                                              */
 | |
| 
 | |
| #define init_exception_context(ec) ((void)((ec)->penv = 0))
 | |
| 
 | |
| #define Try \
 | |
|   { \
 | |
|     jmp_buf *exception__prev, exception__env; \
 | |
|     exception__prev = the_exception_context->penv; \
 | |
|     the_exception_context->penv = &exception__env; \
 | |
|     if (setjmp(exception__env) == 0) { \
 | |
|       do
 | |
| 
 | |
| #define exception__catch(action) \
 | |
|       while (the_exception_context->caught = 0, \
 | |
|              the_exception_context->caught); \
 | |
|     } \
 | |
|     else { \
 | |
|       the_exception_context->caught = 1; \
 | |
|     } \
 | |
|     the_exception_context->penv = exception__prev; \
 | |
|   } \
 | |
|   if (!the_exception_context->caught || action) { } \
 | |
|   else
 | |
| 
 | |
| #define Catch(e) exception__catch(((e) = the_exception_context->v.etmp, 0))
 | |
| #define Catch_anonymous exception__catch(0)
 | |
| 
 | |
| /* Try ends with do, and Catch begins with while(0) and ends with     */
 | |
| /* else, to ensure that Try/Catch syntax is similar to if/else        */
 | |
| /* syntax.                                                            */
 | |
| /*                                                                    */
 | |
| /* The 0 in while(0) is expressed as x=0,x in order to appease        */
 | |
| /* compilers that warn about constant expressions inside while().     */
 | |
| /* Most compilers should still recognize that the condition is always */
 | |
| /* false and avoid generating code for it.                            */
 | |
| 
 | |
| #define Throw \
 | |
|   for (;; longjmp(*the_exception_context->penv, 1)) \
 | |
|     the_exception_context->v.etmp =
 | |
| 
 | |
| 
 | |
| #endif /* CEXCEPT_H */
 | 
