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 Möller . 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.