ex man page on DragonFly

Man page or keyword search:  
man Server   44335 pages
apropos Keyword Search (all sections)
Output format
DragonFly logo
[printable version]

ex(3)			      Exception Handling			 ex(3)

NAME
       OSSP ex - Exception Handling

VERSION
       OSSP ex 1.0.5 (02-Oct-2005)

SYNOPSIS
       ex_t variable;

       ex_try BLOCK1 [ex_cleanup BLOCK2] ex_catch (variable) BLOCK3

       ex_throw(class, object, value);

       ex_rethrow;

       ex_defer BLOCK

       ex_shield BLOCK

       if (ex_catching) ...

       if (ex_deferred) ...

       if (ex_shielding) ...

DESCRIPTION
       OSSP ex is a small ISO-C++ style exception handling library for use in
       the 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.

       EXCEPTIONS

       An OSSP ex exception is a triple <class,object,value> where class iden‐
       tifies the class of the exception thrower, object identifies the par‐
       ticular class instance of the exception thrower, and value is the
       exceptional return code value the thrower wants to communicate. All
       three parts are of type "void *" internally, but every value which can
       be lossless "casted" to this type is usable. Exceptions are created on-
       the-fly by the ex_throw command.

       APPLICATION PROGRAMMER INTERFACE (API)

       The OSSP ex API consists of the following elements:

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

	   void *ex_class
	     This is the class argument of the ex_throw call which created the
	     exception. This can globally and uniquely identify the class to
	     which ex_value belongs to. 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
	     ex_value. It is usually just an additional (optional) information
	     to ex_value.

	   void *ex_object
	     This is the object argument of the ex_throw call which created
	     the exception. This can globally and uniquely identify the class
	     instance ex_value belongs to (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 (optional) information to ex_value.

	   void *ex_value
	     This is the value argument of the 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 returned 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.

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

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

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

       ex_try BLOCK1 [ex_cleanup BLOCK2] ex_catch (variable) BLOCK3
	   This is the primary syntactical construct provided by OSSP ex. It
	   is modeled after the ISO-C++ try-catch clause which in turn is very
	   similar to an ISO-C if-else clause. It consists of an ex_try block
	   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 ex_cleanup block BLOCK2 for performing
	   cleanup operations and an ex_catch block BLOCK3 where the caught
	   exceptions are handled.

	   The control flow in case no exception is thrown is simply BLOCK1,
	   optionally followed by BLOCK2; BLOCK3 is skipped. The control flow
	   in case an exception is thrown is: BLOCK1 (up to the statement
	   where the exception is thrown only), optionally followed by BLOCK2,
	   followed by BLOCK3.

	   The ex_try, ex_cleanup and ex_catch cannot be used separately, they
	   work only in combination because they form a language clause as a
	   whole. In contrast to ISO-C++ there is only one ex_catch block and
	   not multiple ones (all OSSP ex exceptions are of the same ISO-C
	   type ex_t). If an exception is caught, it is stored in variable for
	   inspection inside the ex_catch block. Although having to be
	   declared outside, the variable value is only valid within the
	   ex_catch block. But the variable can be re-used in subsequent
	   ex_catch clauses, of course.

	   The ex_try block is a regular ISO-C language statement block, but
	   it is not allowed to jump into it via "goto" or longjmp(3) or out
	   of it via "break", "return", "goto" or longjmp(3) because there is
	   some hidden setup and cleanup that needs to be done by OSSP ex
	   regardless of whether an exception is caught. Jumping into an
	   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
	   ex_try clauses.

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

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

       ex_throw(class, object, value);
	   This builds an exception from the supplied arguments and throws it.
	   If an ex_try/ex_catch clause formed the dynamic scope of the
	   ex_throw call, this exception is copied into the variable of its
	   ex_catch clause and the program control flow is continued in the
	   (optional ex_cleanup and then in the) ex_catch block. If no
	   ex_try/ex_catch clause exists in the dynamic scope of the ex_throw
	   call, the program calls abort(3). The ex_throw can be performed
	   everywhere, including inside ex_try, ex_cleanup and ex_catch
	   blocks.

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

       ex_defer BLOCK
	   This directive executes BLOCK while deferring the throwing of
	   exceptions, i.e., inside the dynamic scope of ex_defer all ex_throw
	   operations are remembered but deferred and on leaving the BLOCK the
	   first occurred exception is thrown. The second and subsequent
	   exceptions are ignored.

	   The ex_defer block BLOCK is a regular ISO-C language statement
	   block, but it is not allowed to jump into it via "goto" or
	   longjmp(3) or out of it via "break", "return", "goto" or longjmp(3)
	   because this would cause the deferral level to become out of sync.
	   Jumping into an ex_defer clause would avoid increasing the excep‐
	   tion 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 ex_defer clauses.

       ex_shield BLOCK
	   This directive executes BLOCK while shielding it against the throw‐
	   ing of exceptions, i.e., inside the dynamic scope of ex_shield all
	   ex_throw operations are just silently ignored.

	   The ex_shield block is a regular ISO-C language statement block,
	   but it is not allowed to jump into it via "goto" or longjmp(3) or
	   out of it via "break", "return", "goto" or longjmp(3) because this
	   would cause the shielding level to become out of sync.  Jumping
	   into an 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 ex_shield clauses.

       ex_catching
	   This is a boolean flag which can be checked inside the dynamic
	   scope of an ex_try clause to test whether the current scope is
	   exception catching (see ex_try/ex_catch clause).

       ex_deferred
	   This is a boolean flag which can be checked inside the dynamic
	   scope of an ex_defer clause to test whether the current scope is
	   exception deferring (see ex_defer clause).

       ex_shielding
	   This is a boolean flag which can be checked inside the dynamic
	   scope of an ex_shield clause to test whether the current scope is
	   exception shielding (see ex_shield clause).

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

       Machine Context

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

       __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
	   mctx.

       __ex_mctx_save(__ex_mctx_struct *mctx)
	   This is called by the prolog of ex_try to save the current machine
	   context in mctx. This function has to return true (not 0) after
	   saving. If the machine context is restored (by __ex_mctx_restore)
	   it has to return false (0). In other words, this function has to
	   return twice and indicate the particular situation with the pro‐
	   vided return code.

       __ex_mctx_restored(__ex_mctx_struct *mctx)
	   This is called by the epilog of ex_try to perform additional opera‐
	   tions at the new (restored) machine context after an exception was
	   caught. Usually this is a no-operation macro.

       __ex_mctx_restore(__ex_mctx_struct *mctx)
	   This is called by ex_throw at the old machine context in order to
	   restore the machine context of the ex_try/ex_catch clause which
	   will catch the exception.

       The default implementation (define __EX_MCTX_SJLJ__ or as long as
       __EX_MCTX_CUSTOM__ is not defined) uses the 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 __EX_MCTX_SSJLJ__ to use POSIX.1
       sigjmp_buf(3) or __EX_MCTX_MCSC__ to use POSIX.1 ucontext(3). For using
       a custom implementation define __EX_MCTX_CUSTOM__ and provide own defi‐
       nitions for the four __ex_mctx_xxxx macros.

       Exception Context

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

       By default, __ex_ctx (which is __ex_ctx_default as provided by libex)
       returns a pointer to a static ex_ctx_t context. For use in multi-
       threading environments, this should be overwritten with a callback
       function returning a per-thread context structure (see section MULTI‐
       THREADING ENVIRONMENTS below).

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

       Termination Handler

       In case there is an exception thrown which is not caught by any
       ex_try/ex_catch clauses, OSSP ex calls the callback "void (*__ex_termi‐
       nate)(ex_t *)". It receives a pointer to the exception object which was
       thrown.

       By default, __ex_terminate (which is __ex_terminate_default as provided
       by libex) prints a message of the form "**EX: UNCAUGHT EXCEPTION:
       class=0xXXXXXXXX object=0xXXXXXXXX value=0xXXXXXXX [xxxx:NNN:xxxx]" to
       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. Even
       better, a real application always should have a top-level
       ex_try/ex_catch clause in its "main()" in order to more gracefully ter‐
       minate the application.

       Namespace Mapping

       The OSSP ex implementation consistently uses the "ex_", "__ex_" and
       "__EX_" prefixes for namespace protection. But at least the "ex_" pre‐
       fix for the API macros ex_try, ex_cleanup, ex_catch, ex_throw,
       ex_rethrow and ex_shield sometimes have an unpleasant optical appear‐
       ance. Especially because OSSP ex is modeled after the exception facil‐
       ity of ISO-C++ where there is no such prefix on the language direc‐
       tives, of course.

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

PROGRAMMING PITFALLS
       Exception handling is a very elegant and efficient way of dealing with
       exceptional situation. Nevertheless it requires additional discipline
       in programming and there are a few pitfalls one must be aware of. Look
       the following code which shows some pitfalls and contains many errors
       (assuming a mallocex() function which throws an exception if malloc(3)
       fails):

	/* BAD EXAMPLE */
	ex_try {
	    char *cp1, *cp2, cp3;

	    cp1 = mallocex(SMALLAMOUNT);
	    globalcontext->first = cp1;
	    cp2 = mallocex(TOOBIG);
	    cp3 = mallocex(SMALLAMOUNT);
	    strcpy(cp1, "foo");
	    strcpy(cp2, "bar");
	}
	ex_cleanup {
	    if (cp3 != NULL) free(cp3);
	    if (cp2 != NULL) free(cp2);
	    if (cp1 != NULL) free(cp1);
	}
	ex_catch(ex) {
	    printf("cp3=%s", cp3);
	    ex_rethrow;
	}

       This example raises a few issues:

       01: variable scope
	   Variables which are used in the ex_cleanup or ex_catch clauses must
	   be declared before the ex_try clause, otherwise they only exist
	   inside the ex_try block. In the example above, cp1, cp2 and cp3 are
	   automatic variables and only exist in the block of the ex_try
	   clause, the code in the ex_cleanup and ex_catch clauses does not
	   know anything about them.

       02: variable initialization
	   Variables which are used in the ex_cleanup or ex_catch clauses must
	   be initialized before the point of the first possible ex_throw is
	   reached. In the example above, ex_cleanup would have trouble using
	   cp3 if mallocex() throws a exception when allocating a TOOBIG buf‐
	   fer.

       03: volatile variables
	   Variables which are used in the ex_cleanup or ex_catch clauses must
	   be declared with the storage class "volatile", otherwise they might
	   contain outdated information if ex_throw throws an exception.  If
	   using a "free if unset" approach like the example does in the
	   ex_cleanup clause, the variables must be initialized (see 02) and
	   remain valid upon use.

       04: clean before catch
	   The ex_cleanup clause is not only written down before the ex_catch
	   clause, it is also evaluated before the ex_catch clause. So,
	   resources being cleaned up must no longer be used in the ex_catch
	   block. The example above would have trouble referencing the charac‐
	   ter strings in the printf(3) statement because these have been
	   freed before.

       05: variable uninitialization
	   If resources are passed away and out of the scope of the
	   ex_try/ex_cleanup/ex_catch construct and the variables were ini‐
	   tialized for using a "free if unset" approach then they must be
	   uninitialized after being passed away. The example above would
	   free(3) cp1 in the ex_cleanup clause if mallocex() throws an excep‐
	   tion if allocating a TOOBIG buffer. The globalcontext->first
	   pointer hence becomes invalid.

       The following is fixed version of the code (annotated with the pitfall
       items for reference):

	/* GOOD EXAMPLE */
	{ /*01*/
	    char * volatile /*03*/ cp1 = NULL /*02*/;
	    char * volatile /*03*/ cp2 = NULL /*02*/;
	    char * volatile /*03*/ cp3 = NULL /*02*/;
	    try {
		cp1 = mallocex(SMALLAMOUNT);
		globalcontext->first = cp1;
		cp1 = NULL /*05 give away*/;
		cp2 = mallocex(TOOBIG);
		cp3 = mallocex(SMALLAMOUNT);
		strcpy(cp1, "foo");
		strcpy(cp2, "bar");
	    }
	    clean { /*04*/
		printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
		if (cp3 != NULL)
		    free(cp3);
		if (cp2 != NULL)
		    free(cp2);
		/*05 cp1 was given away */
	    }
	    catch(ex) {
		 /*05 global context untouched */
		 rethrow;
	    }
	}

       Alternatively, this could also be used:

	/* ALTERNATIVE GOOD EXAMPLE */
	{ /*01*/
	    char * volatile /*03*/ cp1 = NULL /*02*/;
	    char * volatile /*03*/ cp2 = NULL /*02*/;
	    char * volatile /*03*/ cp3 = NULL /*02*/;
	    try {
		cp1 = mallocex(SMALLAMOUNT);
		globalcontext->first = cp1;
		/*05 keep responsibility*/
		cp2 = mallocex(TOOBIG);
		cp3 = mallocex(SMALLAMOUNT);
		strcpy(cp1, "foo");
		strcpy(cp2, "bar");
	    }
	    clean { /*04*/
		printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
		if (cp3 != NULL)
		    free(cp3);
		if (cp2 != NULL)
		    free(cp2);
		if (cp1 != NULL)
		    free(cp1);
	    }
	    catch(ex) {
		globalcontext->first = NULL;
		rethrow;
	    }
	}

MULTITHREADING ENVIRONMENTS
       OSSP ex is designed to work both in single-threading and multi-thread‐
       ing environments. The default is to support single-threading only. But
       it is easy to configure OSSP ex to work correctly in a multi-threading
       environment like POSIX pthreads or 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 dis‐
       patching mechanism.

       GNU pth

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

       POSIX pthreads

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

       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__ */

       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);
	  }

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

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

       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);

       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;
	  }

       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:

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

       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;
	  }

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

SEE ALSO
       ISO-C++ try, catch, throw.

       Java try, catch, finally, throw.

       ISO-C jmp_buf(3), setjmp(3), longjmp(3).

       POSIX.1 sigjmp_buf(3), sigsetjmp(3), siglongjump(3).

       POSIX.1 ucontext(3), setcontext(3), getcontext(3).

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

       The core try/catch clause was inspired by ISO-C++ and the implementa‐
       tion was partly derived from cexcept 2.0.0, a similar library written
       2000 by Adam M. Costello <amc@cs.berkeley.edu> and Cosmin Truta <cos‐
       min@cs.toronto.edu>.

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

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

02-Oct-2005			 OSSP ex 1.0.5				 ex(3)
[top]

List of man pages available for DragonFly

Copyright (c) for man pages and the logo by the respective OS vendor.

For those who want to learn more, the polarhome community provides shell access and support.

[legal] [privacy] [GNU] [policy] [cookies] [netiquette] [sponsors] [FAQ]
Tweet
Polarhome, production since 1999.
Member of Polarhome portal.
Based on Fawad Halim's script.
....................................................................
Vote for polarhome
Free Shell Accounts :: the biggest list on the net