Exceptions-0.3 - exception facilities for C and Objective-C

Preliminary documentation


INTRODUCTION

Exception handling is something that is missing in C and Objective-C.
This package is an attempt to implement the most important facilities,
in a portable way. Doing this, I have look at how things are done in
elisp, and have also looked a little at C++ (which I, however, don't
know much about).



COPYRIGHT

This package is copyright Niels Mller <nisse@lysator.liu.se>. You may
use, distribute and enhance it freely, under the terms and conditions of the
GNU General Public Licence. See the file COPYING for details.


C INTERFACE

The C interface consists of some macros, CATCH, THROW, PROTECT,
PROTECT0 and PROTECT1, similar to the Emacs Lisp functions 'catch',
'throw' and 'unwind-protect'. These macros use some of the extensions
in GNUC, but the underlying machiniery is very portable, as it is
built on setjmp() and uses an obstack for keeping track of "stack
frames". Before you use them you must call the catch_init() function.

void catch_init(void)
Initializes the stack used for CATCH and THROW.

catch_tag tag;

int result = CATCH(&tag, { body });

THROW(tag, int value);

PROTECT({ body }, { cleanup });

void function0(void);
void function1(void *);
void *arg;

PROTECT0({ body }, function0);
PROTECT1({ body }, function1, arg);


A THROW that happens while executing the body of a CATCH construct
with a matching tag, will cause the CATCH to exit immediately. The
result is then the value passed as argument to THROW (because longjmp
is used, it is impossible to pass the value 0 to through, if you try,
the value 1 is used instead). If no THROW happens, the result is the
value of the last statement in the CATCH body. If you use several
nested CATCH constructs, the tags are guaranteed to be unique.

However, before control is passed from a THROW to the corresponding
CATCH, any cleanup forms established with PROTECT are executed.
PROTECT executes the statements in the body. When the body is
finished, or a THROW out of the protected form happens, the cleanup
statements are executed. Note that by using setjmp directly, it is
possible to bypass the cleanup or make it be called too late, so don't
do this. If you need PROTECT, use only CATCH and THROW for non-local
jumps.

The PROTECT macro requires that your C compiler supports local
functions. For example gcc supports this, for many targets. If you
don't want PROTECT to use local functions, the alternative macros
PROTECT0 and PROTECT1 may be useful.


OBJECTIVE-C INTERFACE

The Objective-C interface the same CATCH, THROW and PROTECT macros as
in C, althoug somewhat differently implemented. However, the
underlying mechanism is easier to use directly. The Objective-C
version also gives you decent error handling. The classes of interest
are Catch, UnwindProtect, CatchError and Error.


Catch
-----

- catch
  Used only together with set_catch().

int result;
id tag = [Catch new];

if ((result = set_catch([tag catch])) == 0) { body }

[tag free]


- throw: result

Actually, you can pass two values back with -throw, one nonzero
integer (passed by the underlying longjmp()), and one object id. The most
general throw method is

- throw: object : (int) number free: (BOOL) freeFlag

The simple -throw method is just an alias for 'throw: result : 17
free: YES' . Another alias,

- throwInt: (int) value
  passes only the integer. It is equivalent to 'throw: nil : value free: NO'

- value
  Extracts the result passed by a -throw.

Normally, you send the throw message to the same object that you used
with -catch, but this is not necessary. When you send -catch to one
object C, and -throw to another object T, the catch matches the throw
if and only if [C isEqual: T] is true (however, T cannot be any
arbitrary object, as it must respond to -throw). Moreover, if C and T
are distinct (that is, if their id:s are distinct), and the freeFlag
passed to -throw is true, T is freed automatically.

- cleanup

Removes the catch from the stack, so that it can no longer be found by
a throw. You usually don't have to send this message, as any necessary
cleanup is performed automatically when you send -catch or -free.


UnwindProtect
-------------

The most important difference between throw and catch on one hand and
the ordinary setjmp() and longjmp() on the other, is the possibility
to register cleanup actions that will be done automatically when a
piece of code is finished or terminated with a throw. The
UnwindProtect class lets you do precisely this.

- new
  Creates an UnweindProtect object, and registers freeing of the
  object as a cleanup action.

- cleanupByCalling: (void (*)(void)) fn
  Register a function to be called without any arguments on cleanup.
  Most useful if you have local functions.

- cleanupByCalling: (void (*)(void *)) fn
  Register a function to be called with one pointer argument.

- cleanupBySending: (SEL) message to: object
  Registers an object and a message to be sent to it.

- (void) cleanup
  Performs all registered cleanup actions, usually including freeing
  the UnwindProtect object itself.

*IMPORTANT* You usually should *NOT* send the -free message to objects
of this class. -free is equivalent to '[self cleanup]; [super free]'
which will free the object twice.

If you want to create an UnwindProtect object, without having it freed
automatically on cleanup, use '[ [UnwindProtect alloc] initDontFree]'
instead of '[UnwindProtect new]'. In this case, use -free when
finished with it.


Error
-----

The Error class is the root of the error hierarchy. It defines two
methods:

- (void) raise
  Raises the error. If there exists a handler for it (see CatchError
  below), it will get control. Otherwise, the program terminates. It
  is the handlers responsibility to free the error object when
  finished with it.

- (const char *) message
  Returns a description of the error.

The package defines two specific classes of errors:

ErrorNoMemory is raised if malloc() fails. It will hopefully do the
right thing, even if the failure happens while adding another
UnwindProtect action. In this case, all cleanup actions will be
performed, including the one that could not be registered properly
because there were no memory left.

ErrorNoCatch is raised if a throw cannot find a matching catch.


CatchError
----------

Used almost as Catch. The differences are:

CatchError doesn't catch -throw, it catches -raise. And the matching mechanism
is different, a CatchError matches all errors that are of a specified
class.

- catch: errorClass
  Like in the Catch class, this method is only used with set_catch.

int result;
id tag = [CatchError new];

if ((result = set_catch([catch: ErrorNoMemory])) == 0) { body }

else /* Handle error */
  {
    id error = [tag error];
    ...
    [error free];
  }
[tag free];

- catch
  With no error class specified implies the same class as for the most
  recent -catch message.

- cleanup
  Like with Catch, you usually don't have to send this message. -catch
  and -free are sufficient.


IMPLEMENTATION

Both the C and the Objective-C uses the same stack, onto which
active catch and protect frames are pushed. There are several types of
frames,

* catch frames. Used by the C CATCH and THROW macros.

* catch_object frames. Used by Objective-C Catch and CatchError objects.

* protect_fn0. Used by the PROTECT macros and the -cleanupByCalling
  methods, for cleanup functions without arguments.

* protect_fn1.  Used by the PROTECT macros and the -cleanupByCalling
  methods, for cleanup functions which require one argument.

* protect_object. Used by the -cleanupBySending: method.

All this means that you can mix several modules, some written in C and
using the C interface with the CATCH and THROW macros, others written
in Objective-C using Catch, UnwindProtect, .. objects. All use the
same stack.

If you C exclusively, link with libccatch.a . If have any Objective-C
code, link with libobjccatch.a instead.
