Blame contrib/visupng/cexcept.h

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