Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
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-&gt;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)

NAME | VERSION | SYNOPSIS | DESCRIPTION | IMPLEMENTATION CONTROL | PROGRAMMING PITFALLS | MULTITHREADING ENVIRONMENTS | EXAMPLES | SEE ALSO | HISTORY | AUTHORS

Want to link to this manual page? Use this URL:
<https://www.freebsd.org/cgi/man.cgi?query=ex&sektion=3&manpath=FreeBSD+12.0-RELEASE+and+Ports>

home | help