OSSP CVS Repository

ossp - Difference in ossp-pkg/ex/ex.pod versions 1.2 and 1.3
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [History

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

CVSTrac 2.0.1