OSSP CVS Repository

ossp - ossp-pkg/ex/ex.pod 1.22
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/ex/ex.pod 1.22
##
##  OSSP ex - Exception Handling Library
##  Copyright (c) 2002 Ralf S. Engelschall <rse@engelschall.com>
##  Copyright (c) 2002 The OSSP Project <http://www.ossp.org/>
##  Copyright (c) 2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
##
##  This file is part of OSSP ex, an exception library
##  which can be found at http://www.ossp.org/pkg/ex/.
##
##  Permission to use, copy, modify, and distribute this software for
##  any purpose with or without fee is hereby granted, provided that
##  the above copyright notice and this permission notice appear in all
##  copies.
##
##  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
##  SUCH DAMAGE.
##
##  ex.pod: exception library manual page
##

=pod

=head1 NAME

B<OSSP ex> - Exception Handling

=head1 VERSION

B<OSSP ex EX_VERSION_STR>

=head1 SYNOPSIS

B<ex_t> I<variable>;

B<ex_try> I<BLOCK1> [B<ex_cleanup> I<BLOCK2>] B<ex_catch> (I<variable>) I<BLOCK3>

B<ex_throw>(I<class>, I<object>, I<value>);

B<ex_rethrow>;

B<ex_defer> I<BLOCK>

B<ex_shield> I<BLOCK>

if (B<ex_catching>) ...

if (B<ex_deferred>) ...

if (B<ex_shielding>) ...

=head1 DESCRIPTION

B<OSSP ex> is a small B<ISO-C++> style exception handling library for
use in the B<ISO-C> language. It allows you to use the paradigm of
throwing and catching exceptions in order to reduce the amount of error
handling code without making your program less robust. 

This is achieved by directly transferring exceptional return codes (and
the program control flow) from the location where the exception is
raised (throw point) to the location where it is handled (catch point)
-- usually from a deeply nested sub-routine to a parent routine. All
intermediate routines no longer have to make sure that the exceptional
return codes from sub-routines are correctly passed back to the parent.

=head2 EXCEPTIONS

An B<OSSP ex> exception is a triple
E<lt>I<class>,I<object>,I<value>E<gt> where I<class> identifies the
class of the exception thrower, I<object> identifies the particular
class instance of the exception thrower, and I<value> is the exceptional
return code value the thrower wants to communicate. All three parts are
of type "C<void *>" internally, but every value is useable which can
be lossless casted this type. Exceptions are created on-the-fly by the
B<ex_throw> command.

=head2 APPLICATION PROGRAMMER INTERFACE (API)

The B<OSSP ex> API consists of the following elements:

=over 4

=item B<ex_t> I<variable>;

This is the declaration of an exception variable. It is usually never
initialized manually. Instead it is initialized by an B<ex_catch> clause
and just used read-only inside its block. Such a variable of type
B<ex_t> consists of six attributes:

=over 2

=item C<void *>I<ex_class>

This is the I<class> argument of the B<ex_throw> call which created
the exception. This should globally and uniquely identify the class of
I<ex_value>. Usually this is a pointer to a static object (variable,
structure or function) which identifies the class of the thrower and
allows the catcher to correctly handle I<ex_value>.

=item C<void *>I<ex_object>

This is the I<object> argument of the B<ex_throw> call which created
the exception. This should globally and uniquely identify the class
instance of I<ex_value> (in case multiple instances exists at all).
Usually this a pointer to a dynamic object (structure) which identifiers
the particular instance of the thrower. It is usually just an additional
information to I<ex_value>.

=item C<void *>I<ex_value>

This is the I<value> argument of the B<ex_throw> call which created
the exception. This is the exceptional return code value which has to
uniquely identify the type of exception. Usually this is the value which
is C<return>ed if no exceptions would be thrown. In the simple case
this is just a numerical return code. In the complex case this can be a
pointer to an arbitrary complex data structure describing the exception.

=item C<char *>I<ex_file> 

This is the file name of the B<ISO-C> source where the B<ex_throw> call
was performed. It is provided as an additional information about the
throw point and is intended mainly for tracing and debugging purposes.

=item C<int> I<ex_line>

This is the line number inside the B<ISO-C> source file name where
the B<ex_throw> call was performed. It is provided as an additional
information about the throw point and is intended mainly for tracing and
debugging purposes.

=item C<char *>I<ex_func>

This is the function name (if determinable, else "C<#NA#>") inside the
B<ISO-C> source file name where the B<ex_throw> call was performed. It
is provided as an additional information about the throw point and is
intended mainly for tracing and debugging purposes.

=back

=item B<ex_try> I<BLOCK1> [B<ex_cleanup> I<BLOCK2>] B<ex_catch> (I<variable>) I<BLOCK3>

This is the primary syntactical construct provided by B<OSSP ex>. It
is modeled after the B<ISO-C++> C<try>-C<catch> clause which in turn
is very similar to an B<ISO-C> C<if>-C<else> clause. It consists of an
B<ex_try> block I<BLOCK1> which forms the dynamic scope for exception
handling (i.e. exceptions directly thrown there or thrown from its
sub-routines are caught), an optional B<ex_cleanup> block I<BLOCK2> for
performing cleanup operations and an B<ex_catch> block I<BLOCK3> where
the caught exceptions are handled.

The control flow in case no exception is thrown is simply I<BLOCK1>,
optionally followed by I<BLOCK2>; I<BLOCK3> is skipped. The control
flow in case an exception is thrown is: I<BLOCK1> (up to the statement
where the exception is thrown only), optionally followed by I<BLOCK2>,
followed by I<BLOCK3>.
    
The B<ex_try>, B<ex_cleanup> and B<ex_catch> cannot be used separately,
they work only in combination because they form a language clause
as a whole. In contrast to B<ISO-C++> there is only one B<ex_catch>
block and not multiple ones (all B<OSSP ex> exceptions are of the same
B<ISO-C> type B<ex_t>). If an exception is caught, it is stored in
I<variable> for inspection inside the B<ex_catch> block. Although having
to be declared outside, the I<variable> value is only valid within
the B<ex_catch> block. But the variable can be re-used in subsequent
B<ex_catch> clauses, of course.

The B<ex_try> block is a regular B<ISO-C> language statement block, but
it is not allowed to jump into it via C<goto> or C<longjmp>(3) or out
of it via C<break>, C<return>, C<goto> or C<longjmp>(3) because there
is some hidden setup and cleanup that needs to be done by B<OSSP ex>
regardless of whether an exception is caught. Jumping into an B<ex_try>
clause would avoid doing the setup, and jumping out of it would avoid
doing the cleanup. In both cases the result is a broken exception
handling facility. Nevertheless you are allowed to nest B<ex_try>
clauses.

The B<ex_cleanup> and B<ex_catch> blocks are regular B<ISO-C> language
statement blocks without any restrictions. You are even allowed to throw
(and in the B<ex_catch> block to re-throw) an exception.

There is just one subtle portability detail you have to remember about
B<ex_try> blocks: all accessible B<ISO-C> objects have the (expected)
values as of the time B<ex_throw> was called, except that the values
of objects of automatic storage invocation duration that do not have
the C<volatile> storage class I<and> have been changed between the
B<ex_try> invocation and B<ex_throw> are indeterminate. This is because
both you usually do not know which commands in the B<ex_try> were
already successful before the exception was thrown (logically speaking)
and because the underlying B<ISO-C> setjmp(3) facility applies those
restrictions (technically speaking).

=item B<ex_throw>(I<class>, I<object>, I<value>);

This builds an exception from the supplied arguments and throws it.
If an B<ex_try>/B<ex_catch> clause formed the dynamic scope of the
B<ex_throw> call, this exception is copied into the I<variable> of its
B<ex_catch> clause and the program control flow is continued in the
B<ex_catch> block. If no B<ex_try>/B<ex_catch> clause exists in the
dynamic scope of the B<ex_throw> call, the program C<abort>(3)s. The
B<ex_throw> can be performed everywhere, including inside B<ex_try> and
B<ex_catch> blocks.

=item B<ex_rethrow>;

This is only valid within an B<ex_catch> block and re-throws
the current exception (in I<variable>). It is similar to the
call B<ex_throw>(I<variable>.ex_class, I<variable>.ex_object,
I<variable>.ex_value) except for the difference that the C<ex_file>,
C<ex_line> and C<ex_func> elements of the caught exception are passed
through as it would have been never caught.

=item B<ex_defer> I<BLOCK>

This directive executes I<BLOCK> while deferring the throwing of
exceptions, i.e., inside the dynamic scope of B<ex_defer> all
B<ex_throw> operations are silently ignored and on leaving the I<BLOCK>
the first occurred exception is thrown.

The B<ex_defer> block is a regular B<ISO-C> language statement block,
but it is not allowed to jump into it via C<goto> or C<longjmp>(3) or
out of it via C<break>, C<return>, C<goto> or C<longjmp>(3) because this
would cause the deferral level to become out of sync. Jumping into
an B<ex_defer> clause would avoid increasing the exception deferral
level, and jumping out of it would avoid decreasing it. In both cases
the result is an incorrect exception deferral level. Nevertheless you
are allowed to nest B<ex_defer> clauses.

=item B<ex_shield> I<BLOCK>

This directive executes I<BLOCK> while shielding it against the throwing
of exceptions, i.e., inside the dynamic scope of B<ex_shield> all
B<ex_throw> operations are silently ignored.

The B<ex_shield> block is a regular B<ISO-C> language statement block,
but it is not allowed to jump into it via C<goto> or C<longjmp>(3) or
out of it via C<break>, C<return>, C<goto> or C<longjmp>(3) because this
would cause the shielding level to become out of sync. Jumping into
an B<ex_shield> clause would avoid increasing the exception shielding
level, and jumping out of it would avoid decreasing it. In both cases
the result is an incorrect exception shielding level. Nevertheless you
are allowed to nest B<ex_shield> clauses.

=item B<ex_catching>

This is a boolean flag which can be checked inside the dynamic scope
of an B<ex_try> clause to test whether the current scope is exception
catching. 

=item B<ex_deferred>

This is a boolean flag which can be checked inside the dynamic scope of
an B<ex_defer> clause to test whether the current scope is exception
deferring.

=item B<ex_shielding>

This is a boolean flag which can be checked inside the dynamic scope of
an B<ex_shield> clause to test whether the current scope is exception
shielding.

=back

=head1 IMPLEMENTATION CONTROL

B<OSSP ex> uses a very light-weight but still flexible exception
facility implementation. The following adjustments can be made before
including the F<ex.h> header:

=head2 Machine Context

In order to move the program control flow from the exception throw point
(B<ex_throw>) to the catch point (B<ex_catch>), B<OSSP ex> uses four
macros:

=over 4

=item B<__ex_mctx_struct>

This holds the contents of the machine context structure. A pointer to
such a machine context is passed to the following macros as I<mctx>.

=item B<__ex_mctx_save>(__ex_mctx_struct *I<mctx>)

This is called by the prolog of B<ex_try> to save the current machine
context in I<mctx>. This function has to return true (not C<0>) after
saving. If the machine context is restored (by B<__ex_mctx_restore>) it
has to return false (C<0>). In other words, this function has to return
twice and indicate the particular situation with the provided return
code.

=item B<__ex_mctx_restored>(__ex_mctx_struct *I<mctx>)

This is called by the epilog of B<ex_try> to perform additional
operations at the new (restored) machine context after an exception was
caught. Usually this is a no-operation macro.

=item B<__ex_mctx_restore>(__ex_mctx_struct *I<mctx>)

This is called by B<ex_throw> at the old machine context in order to
restore the machine context of the B<ex_try>/B<ex_catch> clause which
will catch the exception.

=back

The default implementation (define C<__EX_MCTX_SJLJ__> or as long
C<__EX_MCTX_CUSTOM__> is not defined) uses the B<ISO-C> jmp_buf(3)
facility:

 #define __ex_mctx_struct         jmp_buf jb;
 #define __ex_mctx_save(mctx)     (setjmp((mctx)->jb) == 0)
 #define __ex_mctx_restored(mctx) /* noop */
 #define __ex_mctx_restore(mctx)  (void)longjmp((mctx)->jb, 1)

Alternatively, you can define C<__EX_MCTX_SSJLJ__> to use B<POSIX.1>
sigjmp_buf(3) or C<__EX_MCTX_MCSC__> to use B<POSIX.1> ucontext(3). For
using a custom implementation define C<__EX_MCTX_CUSTOM__> and provide
own definitions for the four B<__ex_mctx_xxxx> macros.

=head2 Exception Context

In order to maintain the exception catching stack and for passing the
exception between the throw and the catch point, B<OSSP ex> uses a
global exception context, returned on-the-fly by the callback "C<ex_ctx_t
*(*>B<__ex_ctx>C<)(void)>".

By default, B<__ex_ctx> (which is B<__ex_ctx_default> as provided by
F<libex>) returns a pointer to a static C<ex_ctx_t> context. For use in
multi-threading environments, this should be overwritten with a callback
function returning a per-thread context structure.

To initialize an exception context structure there are two macros
defined: "B<EX_CTX_INITIALIZER>" for static initialization and
"C<void >B<EX_CTX_INITIALIZE>C<(ex_ctx_t *)>" for dynamic
initialization.

=head2 Termination Handler

In case there is an exception thrown which is not caught by any
B<ex_try>/B<ex_catch> clauses, B<OSSP ex> calls the callback "C<void
(*>B<__ex_terminate>C<)(ex_t *)>". It receives a pointer to the
exception object which should have been thrown.

By default, B<__ex_terminate> (which is B<__ex_terminate_default>
as provided by F<libex>) prints a message of the form "C<**EX:
UNCAUGHT EXCEPTION: class=0xXXXXXXXX object=0xXXXXXXXX value=0xXXXXXXX
[xxxx:NNN:xxxx]>" to F<stderr> and then calls abort(3) in order to
terminate the application. For use in multi-threading environments, this
should be overwritten with a callback function which terminates only the
current thread.

=head2 Namespace Mapping

The B<OSSP ex> implementation consistently uses the "C<ex_>", "C<__ex_>"
and "C<__EX_>" prefixes for namespace protection. But at least
the "C<ex_>" prefix for the API macros B<ex_try>, B<ex_cleanup>,
B<ex_catch>, B<ex_throw>, B<ex_rethrow> and B<ex_shield> sometimes have
an unpleasant optical appearance. Especially because B<OSSP ex> is
modeled after the exception facility of B<ISO-C++> where there is no
such prefix on the language directives, of course.

For this, B<OSSP ex> optionally provides the ability to provide
additional namespace mappings for those API elements. By default (define
C<__EX_NS_CXX__> or as long as C<__EX_NS_CUSTOM__> and C<__cplusplus>
is not defined) you can additionally use the B<ISO-C++> style names
B<catch>, B<cleanup>, B<throw>, B<rethrow> and B<shield>. As an
alternative you can define C<__EX_NS_UCCXX__> to get the same but with a
more namespace safe upper case first letter.

=head1 MULTITHREADING ENVIRONMENTS

B<OSSP ex> is designed to work both in single-threading and
multi-threading environments. The default is to support single-threading
only. But it is easy to configure B<OSSP ex> to work correctly in a
multi-threading environment like B<POSIX pthreads> or B<GNU pth>.

There are only two issues: which machine context to use and where to
store the exception context to make sure exception throwing happens
only within a thread and does not conflict with the regular thread
dispatching mechanism.

=head2 GNU pth

Using B<OSSP ex> together with B<GNU pth> is straight-forward, because
B<GNU pth> 1.5 (and higher) already has support for B<OSSP ex> built-in.
All which is needed is that B<GNU pth> is configured with the B<GNU
Autoconf> option C<--with-ex>. Then each B<GNU pth> user-space thread
has its own B<OSSP ex> exception context automatically. The default
of using B<ISO-C> jmp_buf(3) does not conflict with the thread
dispatching mechanisms used by B<GNU pth>.

=head2 POSIX pthreads

Using B<OSSP ex> inside an arbitrary B<POSIX pthreads> standard
compliant environment is also straight-forward, although it requires
extra coding. What you basically have to do is to make sure that the
B<__ex_ctx> becomes a per-thread context and that B<__ex_terminate>
terminates only the current thread. To get an impression, a small
utility library for this follows:

=over 4

=item F<pthread_ex.h>

 #ifndef __PTHREAD_EX_H__
 #define __PTHREAD_EX_H__

 #include <pthread.h>

 int pthread_init_ex   (void);
 int pthread_create_ex (pthread_t *, const pthread_attr_t *, void *(*)(void *), void *);

 #ifndef PTHREAD_EX_INTERNAL
 #define pthread_init   pthread_init_ex
 #define pthread_create pthread_create_ex
 #endif

 #endif /* __PTHREAD_EX_H__ */

=item F<pthread_ex.c>

 #include <stdlib.h>
 #include <pthread.h>

 #define PTHREAD_EX_INTERNAL
 #include "pthread_ex.h"
 #include "ex.h"

 /* context storage key */
 static pthread_key_t pthread_ex_ctx_key;

 /* context destructor */
 static void pthread_ex_ctx_destroy(void *data)
 {
     if (data != NULL)
         free(data);
     return;
 }
 
 /* callback: context fetching */
 static ex_ctx_t *pthread_ex_ctx(void)
 {
     return (ex_ctx_t *)pthread_getspecific(pthread_ex_ctx_key);
 }
 
 /* callback: termination */
 static void pthread_ex_terminate(ex_t *e)
 {
     pthread_exit(e->ex_value);
 }
  
 /* pthread init */
 int pthread_init_ex(void)
 {
     int rc;
 
     /* additionally create thread data key and override OSSP ex callbacks */
     pthread_key_create(&pthread_ex_ctx_key, pthread_ex_ctx_destroy);
     __ex_ctx       = pthread_ex_ctx;
     __ex_terminate = pthread_ex_terminate;
 
     return rc;
 }
 
 /* internal thread entry wrapper information */
 typedef struct {
     void *(*entry)(void *);
     void *arg;
 } pthread_create_ex_t;
 
 /* internal thread entry wrapper */
 static void *pthread_create_wrapper(void *arg)
 {
     pthread_create_ex_t *wrapper;
     ex_ctx_t *ex_ctx;
 
     /* create per-thread exception context */
     wrapper = (pthread_create_ex_t *)arg;
     ex_ctx = (ex_ctx_t *)malloc(sizeof(ex_ctx_t));
     EX_CTX_INITIALIZE(ex_ctx);
     pthread_setspecific(pthread_ex_ctx_key, ex_ctx);
 
     /* perform original operation */
     return wrapper->entry(wrapper->arg);
 }
 
 /* pthread_create() wrapper */
 int pthread_create_ex(pthread_t *thread, const pthread_attr_t *attr, void *(*entry)(void *), void *arg)
 {
     pthread_create_ex_t wrapper;
 
     /* spawn thread but execute start function through wrapper */
     wrapper.entry = entry;
     wrapper.arg   = arg;
     return pthread_create(thread, attr, pthread_create_wrapper, &wrapper);
 }

=back

Now all which is required is that you include F<pthread_ex.h> after the
standard F<pthread.h> header and to call B<pthread_init> once at startup
of your program.

=head1 EXAMPLES

As a real-life example we will look how you can add optional B<OSSP
ex> based exception handling support to a library B<foo>. The original
library looks like this:

=over 2

=item F<foo.h>

 typedef enum {
     FOO_OK,
     FOO_ERR_ARG,
     FOO_ERR_XXX,
     FOO_ERR_SYS,
     FOO_ERR_IMP,
     ...
 } foo_rc_t;

 struct foo_st;
 typedef struct foo_st foo_t;

 foo_rc_t foo_create  (foo_t **foo);
 foo_rc_t foo_perform (foo_t  *foo);
 foo_rc_t foo_destroy (foo_t  *foo);

=item F<foo.c>

 #include "foo.h"

 struct foo_st {
     ...
 }

 foo_rc_t foo_create(foo_t **foo)
 {
     if ((*foo = (foo_t)malloc(sizeof(foo))) == NULL)
         return FOO_ERR_SYS;
     (*foo)->... = ...
     return FOO_OK;
 }

 foo_rc_t foo_perform(foo_t *foo)
 {
     if (foo == NULL)
         return FOO_ERR_ARG;
     if (...)
         return FOO_ERR_XXX;
     return FOO_OK;
 }

 foo_rc_t foo_destroy(foo_t *foo)
 {
     if (foo == NULL)
         return FOO_ERR_ARG;
     free(foo);
     return FOO_OK;
 }

=back

Then the typical usage of this library is:

 #include "foo.h"
 ...
 foo_t foo;
 foo_rc_t rc;
 ...
 if ((rc = foo_create(&foo)) != FOO_OK)
     die(rc);
 if ((rc = foo_perform(foo)) != FOO_OK)
     die(rc);
 if ((rc = foo_destroy(foo)) != FOO_OK)
     die(rc);

But what you really want, is to use exception handling to get rid of the
intermixed error handling code:

 #include "foo.h"
 #include "ex.h"
 ...
 foo_t foo;
 ex_t ex;
 ...
 ex_try {
     foo_create(&foo);
     foo_perform(foo);
     foo_destroy(foo);
 }
 ex_catch (ex) {
     die((foo_rc_t)ex->ex_value);
 }

You can achieve this very easily by changing the library as following:

=over 4

=item F<foo.h>

 ...
 extern const char foo_id[];
 ...

=item F<foo.c>

 #include "foo.h"

 const char foo_id[] = "foo 1.0";
 
 #ifdef WITH_EX
 #include "ex.h"
 #define FOO_RC(rv) \
     (  (rv) != FOO_OK && (ex_catching && !ex_shielding) \
      ? (ex_throw(foo_id, NULL, (rv)), (rv)) : (rv) )
 #else
 #define FOO_RC(rv) (rv)
 #endif

 struct foo_st {
     ...
 }

 foo_rc_t foo_create(foo_t **foo)
 {
     if ((*foo = (foo_t)malloc(sizeof(foo))) == NULL)
         return FOO_RC(FOO_ERR_SYS);
     (*foo)->... = ...
     return FOO_OK;
 }

 foo_rc_t foo_perform(foo_t *foo)
 {
     if (foo == NULL)
         return FOO_RC(FOO_ERR_ARG);
     if (...)
         return FOO_RC(FOO_ERR_XXX);
     return FOO_OK;
 }

 foo_rc_t foo_destroy(foo_t *foo)
 {
     if (foo == NULL)
         return FOO_RC(FOO_ERR_ARG);
     free(foo);
     return FOO_OK;
 }

=back

This way the library by default is still exactly the same. If you now
compile it with C<-DWITH_EX> you activate exception handling support.
This means that all API functions throw exceptions where C<ex_value> is
the C<foo_rc_t> instead of returning this value.

=head1 SEE ALSO

B<ISO-C++> C<try>, C<catch>, C<throw>.

B<Java> C<try>, C<catch>, C<finally>, C<throw>.

B<ISO-C> jmp_buf(3), setjmp(3), longjmp(3).

B<POSIX.1> sigjmp_buf(3), sigsetjmp(3), siglongjump(3).

B<POSIX.1> ucontext(3), setcontext(3), getcontext(3).

=head1 HISTORY

B<OSSP ex> was invented in January 2002 by Ralf S. Engelschall
E<lt>rse@engelschall.comE<gt> for use inside the B<OSSP> project. Its
creation was prompted by the requirement to reduce the error handling
inside B<OSSP lmtp2nntp>.

The core B<try>/B<catch> clause was inspired by B<ISO-C++> and the
implementation was partly derived from B<cexcept> 2.0.0, a similar
library written 2000 by Adam M. Costello E<lt>amc@cs.berkeley.eduE<gt>
and Cosmin Truta E<lt>cosmin@cs.toronto.eduE<gt>.

The B<cleanup> clause was inspired by the B<Java> C<finally> clause.
The B<shield> feature was inspired by an C<errno> shielding facility
used in the B<GNU pth> implementation. The B<defer> feature was invented
to simplify an application's cleanup handling if multiple independent
resources are allocated and have to be freed on error.

=head1 AUTHORS

 Ralf S. Engelschall
 rse@engelschall.com
 www.engelschall.com

=cut


CVSTrac 2.0.1