OSSP CVS Repository

ossp - Check-in [1641]
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [Patchset]  [Tagging/Branching

Check-in Number: 1641
Date: 2002-Jan-25 23:23:17 (local)
2002-Jan-25 22:23:17 (UTC)
User:rse
Branch:
Comment: many many polishing and extending
Tickets:
Inspections:
Files:
ossp-pkg/ex/TODO      1.2 -> 1.3     3 inserted, 3 deleted
ossp-pkg/ex/ex.h      1.3 -> 1.4     110 inserted, 55 deleted
ossp-pkg/ex/ex.pod      1.2 -> 1.3     416 inserted, 17 deleted
ossp-pkg/ex/ex_test.c      1.1 -> 1.2     6 inserted, 2 deleted

ossp-pkg/ex/TODO 1.2 -> 1.3

--- TODO 2002/01/25 18:32:25     1.2
+++ TODO 2002/01/25 22:23:17     1.3
@@ -1,3 +1,3 @@
-- default handler
-- pth
-- disable throwing
+- devtool environment
+- test thread examples 
+- overhault ex_text.c


ossp-pkg/ex/ex.h 1.3 -> 1.4

--- ex.h 2002/01/25 18:36:17     1.3
+++ ex.h 2002/01/25 22:23:17     1.4
@@ -26,9 +26,13 @@
 #ifndef __EX_H__
 #define __EX_H__
 
-/* the required ISO-C standard facilities */
-#include <stdio.h>   /* for NULL */
-#include <setjmp.h>  /* for jmp_buf, setjmp(3), longjmp(3) */
+/* required ISO-C standard facilities */
+#include <stdio.h>
+
+/* convinience define */
+#ifndef NULL
+#define NULL (void *)0
+#endif
 
 /* determine how the current function name can be fetched */
 #if (   defined(__STDC__) \
@@ -45,19 +49,30 @@
 #endif
 
 /* the machine context */
-#ifndef __ex_mctx_st
-#define __ex_mctx_st             struct { jmp_buf jb; }
-#endif
-#ifndef __ex_mctx_save
+#if defined(__EX_MCTX_USE_MCSC__)
+#include <ucontext.h>            /* POSIX.1 ucontext(3) */
+#define __ex_mctx_struct         ucontext_t uc; 
+#define __ex_mctx_save(mctx)     (getcontext(&(mctx)->uc) == 0)
+#define __ex_mctx_restored(mctx) /* noop */
+#define __ex_mctx_restore(mctx)  (void)setcontext(&(mctx)->uc)
+
+#elif defined(__EX_MCTX_USE_SSJLJ__)
+#include <setjmp.h>              /* POSIX.1 sigjmp_buf(3) */
+#define __ex_mctx_struct         sigjmp_buf jb;
+#define __ex_mctx_save(mctx)     (sigsetjmp((mctx)->jb, 1) == 0)
+#define __ex_mctx_restored(mctx) /* noop */
+#define __ex_mctx_restore(mctx)  (void)siglongjmp((mctx)->jb, 1)
+
+#elif defined(__EX_MCTX_USE_SJLJ__) || !defined(__EX_MCTX_USE_CUSTOM__)
+#include <setjmp.h>              /* ISO C jmp_buf(3) */
+#define __ex_mctx_struct         jmp_buf jb;
 #define __ex_mctx_save(mctx)     (setjmp((mctx)->jb) == 0)
-#endif
-#ifndef __ex_mctx_restore
+#define __ex_mctx_restored(mctx) /* noop */
 #define __ex_mctx_restore(mctx)  (void)longjmp((mctx)->jb, 1)
 #endif
-#ifndef __ex_mctx_restored
-#define __ex_mctx_restored(mctx) /* NOOP */
-#endif
-typedef __ex_mctx_st __ex_mctx_t;
+
+/* declare the machine context type */
+typedef struct { __ex_mctx_struct } __ex_mctx_t;
 
 /* declare the exception type (public) */
 typedef struct {
@@ -71,34 +86,65 @@
     char *ex_func;
 } ex_t;
 
-/* declare the exception context type (private) */
+/* declare the context type (private) */
 typedef struct {
-    __ex_mctx_t  *ctx_mctx_prev; /* previous jump buffer */
-    int           ctx_caught;    /* flag whether exception was caught */
-    volatile ex_t ctx_ex;        /* the exception temporary storage */
+    __ex_mctx_t  *ctx_mctx;      /* permanent machine context of enclosing try/catch */
+    int           ctx_disabled;  /* permanent flag whether exception handling is disabled */
+    int           ctx_caught;    /* temporary flag whether exception was caught */
+    volatile ex_t ctx_ex;        /* temporary exception storage */
 } ex_ctx_t;
 
-/* exception context */
-#ifndef __ex_ctx
+/* the static and dynamic initializers for a context structure */
+#define EX_CTX_INITIALIZER \
+    { NULL, 0, 0, { NULL, NULL, NULL, NULL, 0, NULL } }
+#define EX_CTX_INITIALIZE(ctx) \
+    do { \
+        (ctx)->ctx_mctx         = NULL; \
+        (ctx)->ctx_disabled     = 0;    \
+        (ctx)->ctx_caught       = 0;    \
+        (ctx)->ctx_ex.ex_class  = NULL; \
+        (ctx)->ctx_ex.ex_object = NULL; \
+        (ctx)->ctx_ex.ex_value  = NULL; \
+        (ctx)->ctx_ex.ex_file   = NULL; \
+        (ctx)->ctx_ex.ex_line   = 0;    \
+        (ctx)->ctx_ex.ex_func   = NULL; \
+    } while (0)
+
+/* the exception context */
+#if defined(__EX_CTX_USE_STATIC__)
+static ex_ctx_t __ex_ctx_global;
+#define __ex_ctx (&__ex_ctx_global)
+#elif defined(__EX_CTX_USE_GLOBAL__) || !(__EX_CTX_USE_CUSTOM__)
+#define EX_CTX_GLOBAL ex_ctx_t __ex_ctx_global;
+extern ex_ctx_t __ex_ctx_global;
 #define __ex_ctx (&__ex_ctx_global)
-#ifdef __EX_STATIC_CTX__
-static ex_ctx_t __ex_ctx_global; /* for very small environments */
-#else
-extern ex_ctx_t __ex_ctx_global; /* for non-MT environments (require libex.a) */
 #endif
+
+/* the termination handler */
+#if defined(__EX_TERMINATE_USE_NOOP__)
+#define __ex_terminate(e) \
+    /* noop */
+#elif defined(__EX_TERMINATE_USE_ABORT__) || !(__EX_CTX_USE_CUSTOM__)
+#define __ex_terminate(e) \
+    ( fprintf(stderr, \
+              "**EX: UNCAUGHT EXCEPTION: " \
+              "class=0x%lx object=0x%lx value=0x%lx [%s:%d@%s]\n", \
+              (long)((e)->ex_class), (long)((e)->ex_object), (long)((e)->ex_value), \
+              (e)->ex_file, (e)->ex_line, (e)->ex_func), \
+     abort() )
 #endif
 
-/* block for trying execution */
+/* the block for trying execution */
 #define ex_try \
     { \
-        __ex_mctx_t *__ex_mctx_prev; \
+        __ex_mctx_t *__ex_mctx_en; \
         __ex_mctx_t __ex_mctx_me; \
-        __ex_mctx_prev = __ex_ctx->ctx_mctx_prev; \
-        __ex_ctx->ctx_mctx_prev = &__ex_mctx_me; \
+        __ex_mctx_en = __ex_ctx->ctx_mctx; \
+        __ex_ctx->ctx_mctx = &__ex_mctx_me; \
         if (__ex_mctx_save(&__ex_mctx_me)) { \
             if (1)
 
-/* block for catching an exception */
+/* the block for catching an exception */
 #define ex_catch(e) \
             else { \
             } \
@@ -108,46 +154,55 @@
             __ex_mctx_restored(&__ex_mctx_me); \
             __ex_ctx->ctx_caught = 1; \
         } \
-        __ex_ctx->ctx_mctx_prev = __ex_mctx_prev; \
+        __ex_ctx->ctx_mctx = __ex_mctx_en; \
     } \
-    if (!__ex_ctx->ctx_caught || ((e) = __ex_ctx->ctx_ex, 0)) { \
+    if (   !(__ex_ctx->ctx_caught) \
+        || ((e) = __ex_ctx->ctx_ex, 0)) { \
     } \
     else
 
-/* throw a new exception */
+/* the throwing of a new exception */
 #define ex_throw(c,o,v) \
-    (__ex_ctx->ctx_mctx_prev == NULL ? \
-     (abort(), 0) : \
-     ( __ex_ctx->ctx_ex.ex_class  = (void *)(c), \
-       __ex_ctx->ctx_ex.ex_object = (void *)(o), \
-       __ex_ctx->ctx_ex.ex_value  = (void *)(v), \
-       __ex_ctx->ctx_ex.ex_file   = __FILE__, \
-       __ex_ctx->ctx_ex.ex_line   = __LINE__, \
-       __ex_ctx->ctx_ex.ex_func   = __EX_FUNC__, \
-       __ex_mctx_restore(__ex_ctx->ctx_mctx_prev), \
-       0 \
-     ) \
-    )
+    (__ex_ctx->ctx_disabled ? 0 : \
+     (__ex_ctx->ctx_ex.ex_class  = (void *)(c), \
+      __ex_ctx->ctx_ex.ex_object = (void *)(o), \
+      __ex_ctx->ctx_ex.ex_value  = (void *)(v), \
+      __ex_ctx->ctx_ex.ex_file   = __FILE__, \
+      __ex_ctx->ctx_ex.ex_line   = __LINE__, \
+      __ex_ctx->ctx_ex.ex_func   = __EX_FUNC__, \
+      (  __ex_ctx->ctx_mctx == NULL \
+       ? (__ex_terminate(&(__ex_ctx->ctx_ex)), -1) \
+       : (__ex_mctx_restore(__ex_ctx->ctx_mctx), 1) )))
 
-/* re-throw a caught exception */
+/* the re-throwing of an already caught exception */
 #define ex_rethrow \
-    (__ex_ctx->ctx_mctx_prev == NULL ? \
-     (abort(), 0) : \
-     (__ex_mctx_restore(__ex_ctx->ctx_mctx_prev), 0) \
-    )
+    (__ex_ctx->ctx_disabled ? 0 : \
+      (__ex_ctx->ctx_mctx == NULL ? (__ex_terminate(&(__ex_ctx->ctx_ex)), -1) : \
+        (__ex_mctx_restore(__ex_ctx->ctx_mctx), 1) ))
+
+/* shield an operation from exception handling */
+#define ex_shield \
+    for (__ex_ctx->ctx_disabled = 1; \
+         __ex_ctx->ctx_disabled == 1; \
+         __ex_ctx->ctx_disabled = 0)
+#define ex_shielded \
+        (__ex_ctx->ctx_disabled)
 
 /* optional namespace mapping */
-#if !defined(__cplusplus) && !defined(__EX_NO_CXX_NS__)
-#define try      ex_try
-#define catch    ex_catch
-#define throw    ex_throw
-#define rethrow  ex_rethrow
-#endif
-#if !defined(__EX_NO_SIMPLE_NS__)
+#if defined(__EX_NS_USE_UCCXX__)
 #define Try      ex_try
 #define Catch    ex_catch
 #define Throw    ex_throw
 #define Rethrow  ex_rethrow
+#define Shield   ex_shield
+#define Shielded ex_shielded
+#elif defined(__EX_NS_USE_CXX__) || (!defined(__cplusplus) && !defined(__EX_NS_USE_CUSTOM__))
+#define try      ex_try
+#define catch    ex_catch
+#define throw    ex_throw
+#define rethrow  ex_rethrow
+#define shield   ex_shield
+#define shielded ex_shielded
 #endif
 
 #endif /* __EX_H__ */


ossp-pkg/ex/ex.pod 1.2 -> 1.3

--- ex.pod       2002/01/25 15:30:05     1.2
+++ ex.pod       2002/01/25 22:23:17     1.3
@@ -39,6 +39,10 @@
 
 B<ex_rethrow>;
 
+B<ex_shield> { ... };
+
+if (B<ex_shielded>) ...
+
 =head1 DESCRIPTION
 
 B<OSSP ex> is a small ISO-C++ style exception handling library for use
@@ -173,30 +177,424 @@
 I<variable>.ex_value) but with the difference that the C<ex_file>,
 C<ex_line> and C<ex_func> elements of the thrown exception are kept.
 
+=item B<ex_shield> { ... }
+
+This directive executes its block while shielding against the throwing
+of exceptions, i.e., in the dynamic scope of B<ex_shield> any
+B<ex_throw> operation is ignored. 
+
+=item B<ex_shielded>
+
+This is a flag which can be tested inside a block to test whether the
+current scope is exception shielded (by B<ex_shield> somewhere in the
+dynamic scope) or not.
+
+=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 is 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>(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>).
+
+=item B<__ex_mctx_restored>(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
+cought. Usually this is a no-operation macro.
+
+=item B<__ex_mctx_restore>(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
 
-=head1 EXAMPLE
+The default implementation (define C<__EX_MCTX_USE_SJLJ__> or as long
+C<__EX_MCTX_USE_CUSTOM__> is not defined) uses B<ISO C> jmp_buf(3):
 
+ #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 one can define C<__EX_MCTX_USE_SSJLJ__> to use B<POSIX.1>
+sigjmp_buf(3) and C<__EX_MCTX_USE_MCSC__> to use B<POSIX.1> ucontext(3).
+For using a custom implementation define C<__EX_MCTX_USE_CUSTOM__> and
+provide own macro definitions for the four B<__ex_mctx_xxxx>.
+
+=head2 Exception Context
+
+In order to maintain the exception catching stack and for passing the
+exception between the throw point and the catch point, B<OSSP ex> uses
+a global exception context macro B<__ex_ctx> holding a pointer to a
+structure of type B<ex_ctx_t>. This is a private data structure and
+should be treated as opaque by the user. 
+
+By default (define C<__EX_CTX_USE_GLOBAL__> or as long as
+C<__EX_CTX_USE_CUSTOM__> is not defined) this references a global
+variable C<__ex_ctx_global>. This variable is either defined by the user
+by placing the directive "C<EX_CTX_GLOBAL>" macro somewhere outside of
+any functions in the application or alternatively link the F<libex>
+library to the application.
+
+Alternatively one can define C<__EX_CTX_USE_STATIC__> to get a static
+declaration directly inside the header file (for small environments
+where F<ex.h> is included once only) or define C<__EX_CTX_USE_CUSTOM__>
+and provide an own macro definition for B<__ex_ctx>.
+
+To initialize an exception context structure there are two macros
+defined: B<EX_CTX_INITIALIZER> for static initialization and
+B<EX_CTX_INITIALIZE>(B<ex_ctx_t *>I<ctx>) 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 macro
+B<__ex_terminate>(C<ex_t *>). It receives a pointer to the exception
+object which should have been thrown.
+
+By default (define C<__EX_TERMINATE_USE_ABORT__> or as long as
+C<__EX_TERMINATE_USE_CUSTOM__> is not defined) this prints a message of
+the form "C<**EX: UNCAUGHT EXCEPTION: class=0xXXXXXXXX object=0xXXXXXXXX
+value=0xXXXXXXX [file:123:func]>" to F<stderr> and then calls abort(3)
+in order to terminate the application.
+
+Alternatively one can define C<__EX_TERMINATE_USE_NOOP__> to ignore the
+exception or or define C<__EX_TERMINATE_USE_CUSTOM__> and provide an own
+macro definition for B<__ex_terminate>.
+
+=head2 Namespace Mapping
+
+B<OSSP ex> implementation consistently uses the B<ex_>, B<__ex_>
+and B<__EX_> prefixes for namespace protection. But at least the
+B<ex_> prefix for the API macros B<ex_try>, B<ex_catch>, B<ex_throw>,
+B<ex_rethrow>, B<ex_shield> and B<ex_shielded> sometimes have a
+unpleasant optical appearance. Especially because B<OSSP rc> is modeled
+after the exception facility in ISO C++ where there is no such prefix on
+the directives.
+
+For this B<OSSP ex> optionally provides the ability to provide
+additional namespace mappings for those API macros. By default (define
+C<__EX_NS_USE_CXX__> o or as long as C<__EX_CTX_USE_CUSTOM__> and
+C<__cplusplus> is not defined) you can additional C++ style macros
+named B<catch>, B<throw>, B<rethrow>, B<shield> and B<shielded>. As an
+alternative you can define C<__EX_NS_USE_UCCXX__> to get the same but
+with an (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 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. Here are two examples:
+
+=head2 GNU pth
+
+Using B<OSSP ex> with B<GNU pth> is straight-forward. One just have to
+wrap pth_init() and pth_spawn() to tie the exception facility to the
+threading facility.
+
+=over 4
+
+=item F<pth_ex.h>
+
+ #ifndef __PTH_EX_H__
+ #define __PTH_EX_H__
+
+ /* include GNU pth API */
+ #include "pth.h"
+ 
+ /* configure and include OSSP ex API */
+ #define __EX_CTX_USE_CUSTOM__
+ #define __ex_ctx \
+     (ex_ctx_t *)pth_getkey(ex_ctx_key)
+ #define __ex_terminate(e) \
+     pth_exit(e->ex_value)
  #include "ex.h"
 
- ex_t e;
+ int pth_init_ex(void);
+ pth_t pth_spawn_ex(pth_attr_t attr, void *(*entry)(void *), void *arg);
 
- ex_try {
+ #ifndef PTH_EX_C
+ #define pth_init  pth_ex_init
+ #define pth_spawn pth_ex_spawn
+ #endif
+
+ #endif /* __PTH_EX_H__ */
+
+=item F<pth_ex.c>
+
+ #define PTH_EX_C
+ #include "pth_ex.h"
+
+ /* context storage key */
+ static pth_key_t ex_ctx_key;
+
+ /* context destructor */
+ void ex_ctx_destroy(void *data)
+ {
+     if (data != NULL)
+         free(data);
+     return;
+ }
+
+ /* pth_init() wrapper */
+ int pth_init_ex(void)
+ {
+     int rc;
+
+     rc = pth_init();
+     pth_key_create(&ex_ctx_key, ex_ctx_destroy);
+     return rc;
+ }
+
+ /* internal thread entry wrapper information */
+ typedef struct {
+     void *(*entry)(void *);
+     void *arg;
+ } pth_spawn_ex_t;
+
+ /* internal thread entry wrapper */
+ static void *pth_spawn_wrapper(void *arg)
+ {
+     pth_spawn_ex_t *wrapper;
+     ex_ctx_t *ex_ctx;
+
+     wrapper = (pth_spawn_ex_t *)arg;
+     ex_ctx = (ex_ctx_t *)malloc(sizeof(ex_ctx_t));
+     EX_CTX_INITIALIZE(ex_ctx);
+     pth_key_setdata(ex_ctx_key, ex_ctx);
+     return wrapper.entry(wrapper.arg);
+ }
+
+ /* pth_spawn() wrapper */
+ pth_t pth_spawn_ex(pth_attr_t attr, void *(*entry)(void *), void *arg)
+ {
+     pth_t tid;
+     pth_spawn_ex_t wrapper;
+
+     wrapper.entry = entry;
+     wrapper.arg   = arg;
+     tid = pth_spawn(attr, pth_spawn_wrapper, &wrapper);
+     return tid;
+ }
+
+=back
+
+=head2 POSIX pthreads
+
+Using B<OSSP ex> inside a B<POSIX pthreads> environment is identical to
+using it inside B<GNU pth>. You can use the same solution as above by
+just replacing all B<pth_> prefixes with B<pthread_> prefixes.
+
+=head1 EXAMPLES
+
+As a full 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,
      ...
-     ex_throw (..., ..., 123);
+ } 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 {
      ...
  }
- ex_catch (e) {
-     ...
-     if ((int)e->ex_value == 123)
-         ...
-     else if ((int)e->ex_value == 456)
-         ...
-     else
-         ex_rethrow;
+
+ 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>
+
+ ...
+ foo_rc_t foo_ex(foo_t *foo, int use);
+ ...
+
+=item F<foo.c>
+
+ #include "foo.h"
+
+ #ifdef WITH_EX
+ #include "ex.h"
+ char foo_ex_class = "foo"; /* class identifier */
+ int  foo_ex_use   = 0;     /* class disable flag */
+ #endif
+ 
+ struct foo_st {
+ #ifdef WITH_EX
+     int ex_use; /* object disable flag */
+ #endif
      ...
  }
 
+ #ifdef WITH_EX
+ #define FOO_ERR(ctx,err) \
+     ((   ex_shielded \
+       || !foo_ex_use \
+       || ((ctx) != NULL && !(ctx)->ex_use)) 
+      ? FOO_ERR_##err \
+      : ex_throw(&foo_ex_class, ctx, FOO_ERR_##err))
+ #else
+ #define FOO_ERR(ctx,err) \
+      (FOO_ERR_##err)
+ #endif
+
+ foo_rc_t foo_create(foo_t **foo)
+ {
+     if ((*foo = (foo_t)malloc(sizeof(foo))) == NULL)
+         return FOO_ERR(NULL, SYS);
+     (*foo)->ex_use = 0;
+     (*foo)->... = ...
+     return FOO_OK;
+ }
+
+ foo_rc_t foo_ex(foo_t *foo, int use)
+ {
+     if (foo == NULL)
+         return FOO_ERR(foo, ARG);
+#ifndef WITH_EX
+     return FOO_ERR_IMP;
+#else
+     foo->ex_use = use;
+     return FOO_OK;
+#endif
+ }
+
+ foo_rc_t foo_perform(foo_t *foo)
+ {
+     if (foo == NULL)
+         return FOO_ERR(foo, ARG);
+     if (...)
+         return FOO_ERR(foo, XXX);
+     return FOO_OK;
+ }
+
+ foo_rc_t foo_destroy(foo_t *foo)
+ {
+     if (foo == NULL)
+         return FOO_ERR(foo, 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 build optional exception handling
+support into it, although it is still disabled and the library behaviour
+is the same. But if you now enable execptions via C<foo_ex(foo, 1);>
+the library throws exceptions where C<ex_value> is the C<foo_rc_t>
+instead of returning this value. Notice that in our example we allow the
+enabling/disabling also on a per object basis, i.e., some B<foo> objects
+could have exception handling enabled and others not.
+
 =head1 SEE ALSO
 
 C++ try/catch/throw language directives;
@@ -204,11 +602,12 @@
 
 =head1 HISTORY
 
-B<OSSP ex> was invented in January 2002 by Ralf S. Engelschall for use
-inside the OSSP project. Its creation was prompted by the requirement to
-reduce the error handling inside B<OSSP lmtp2nntp>. The implementation
-is 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
+<OSSP ex> was invented in January 2002 by Ralf S. Engelschall
+for use inside the 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 implementation is
+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>.
 
 =head1 AUTHORS


ossp-pkg/ex/ex_test.c 1.1 -> 1.2

--- ex_test.c    2002/01/25 15:25:51     1.1
+++ ex_test.c    2002/01/25 22:23:17     1.2
@@ -60,13 +60,15 @@
 {
     srandom((unsigned int)time(NULL));
     ex_t e;
-    
+
     fprintf(stderr, "main-0\n");
     for (;;) {
         fprintf(stderr, "main-1\n");
         try {
             fprintf(stderr, "main-2\n");
-            foo_func();
+            ex_shield {
+                foo_func();
+            }
             fprintf(stderr, "main-3\n");
             for (;;) {
                 fprintf(stderr, "main-4\n");
@@ -96,6 +98,8 @@
             fprintf(stderr, "   %s@%s:%d class=0x%lx object=0x%lx value=0x%lx\n", 
                     e.ex_func, e.ex_file, e.ex_line, (long)e.ex_class, (long)e.ex_object, (long)e.ex_value);
             fprintf(stderr, "main-12\n");
+            if (e.ex_class == FOO_CLASS && (foo_rc_t)(e.ex_value) == FOO_ERR_2)
+                break;
         }
     }
     fprintf(stderr, "main-0\n");

CVSTrac 2.0.1