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 ex-
       ceptional 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 in-
	     formation about the throw point and is intended mainly for	 trac-
	     ing 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 de-
	   clared 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 re-
	   gardless 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  ex-
	   ception  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 (ex-
	   pected) 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  be-
	   cause  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 ap-
	   plies 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 el-
	   ements 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 ex-
	   ceptions, 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 ex-
	   ceptions 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  ex-
	   ception 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 de-
       fined:  "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 de-
       fined) you can additionally use the ISO-C++ style names catch, cleanup,
       throw, rethrow and shield. As an	alternative you	can define __EX_NS_UC-
       CXX__ 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 in-
	   side	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
	   buffer.

       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,  re-
	   sources  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 op-
       tion --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 en-
       vironment is also straight-forward, although it requires	extra  coding.
       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 cur-
       rent 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@en-
       gelschall.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 ap-
       plication's cleanup handling if multiple	independent resources are  al-
       located 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)

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

home | help