%except(python) { try { $function } catch (RangeError) { PyErr_SetString(PyExc_IndexError,"index out-of-bounds"); return NULL; } }
As an argument, you need to specify the target language. The exception handling C/C++ code is then enclosed in braces. The symbol $function is replaced with the real C/C++ function call that SWIG would be ordinarily make in the wrapper code. The C code you specify inside the %except directive can be anything you like including custom C code and C++ exceptions.
To delete an exception handler, simply use the %except directive with no code. For example :
Exceptions can be redefined as necessary. The scope of an exception handler is from the point of definition to the end of the file, the definition of a new exception handler, or until the handler is deleted.%except(python); // Deletes any previously defined handler
To work, functions will need to explicitly call throw_exception() to indicate an error occurred. For example :/* File : except.c */ static char error_message[256]; static int error_status = 0; void throw_exception(char *msg) { strncpy(error_message,msg,256); error_status = 1; } void clear_exception() { error_status = 0; } char *check_exception() { if (error_status) return error_message; else return NULL; }
To catch the exception, you can write a simple exception handler such as the following (shown for Perl5) :double inv(double x) { if (x != 0) return 1.0/x; else { throw_exception("Division by zero"); return 0; } }
%except(perl5) { char *err; clear_exception(); $function if ((err = check_exception())) { croak(err); } }
Now, when an error occurs, it will be translated into a Perl error. The downside to this approach is that it isn't particularly clean and it assumes that your C code is a willing participant in generating error messages. (This isn't going to magically add exceptions to a code that doesn't have them).
Now, within a C program, you can do the following :/* File : except.c Just the declaration of a few global variables we're going to use */ #include <setjmp.h> jmp_buf exception_buffer; int exception_status; /* File : except.h */ #include <setjmp.h> extern jmp_buf exception_buffer; extern int exception_status; #define try if ((exception_status = setjmp(exception_buffer)) == 0) #define catch(val) else if (exception_status == val) #define throw(val) longjmp(exception_buffer,val) #define finally else /* Exception codes */ #define RangeError 1 #define DivisionByZero 2 #define OutOfMemory 3
Finally, to create a SWIG exception handler, write the following :double inv(double x) { if (x) return 1.0/x; else {throw(DivisionByZero); }
%{ #include "except.h" %} %except(perl5) { try { $function } catch(RangeError) { croak("Range Error"); } catch(DivisionByZero) { croak("Division by zero"); } catch(OutOfMemory) { croak("Out of memory"); } finally { croak("Unknown exception"); } }
At this point, you're saying this sure looks alot like C++ and you'd be right (C++ exceptions are often implemented in a similar manner). As always, the usual disclaimers apply--your mileage may vary.
The exception types need to be declared as classes elsewhere, possibly in a header file :%except(perl5) { try { $function } catch(RangeError) { croak("Range Error"); } catch(DivisionByZero) { croak("Division by zero"); } catch(OutOfMemory) { croak("Out of memory"); } catch(...) { croak("Unknown exception"); } }
Newer versions of the SWIG parser should ignore exceptions specified in function declarations. For example :class RangeError {}; class DivisionByZero {}; class OutOfMemory {};
double inv(double) throw(DivisionByZero);
%except(python) { ... your exception handler ... } /* Define critical operations that can throw exceptions here */ %except(python); // Clear the exception handler /* Define non-critical operations that don't throw exceptions */
When applied, this will automatically check the return value of malloc() and raise an exception if it's invalid. For example :%typemap(python,except) void * { $function if (!$source) { PyExc_SetString(PyExc_MemoryError,"Out of memory in $name"); return NULL; } } void *malloc(int size);
Python 1.4 (Jan 16 1997) [GCC 2.7.2] Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>> from example import * >>> a = malloc(2048) >>> b = malloc(1500000000) Traceback (innermost last): File "<stdin>", line 1, in ? MemoryError: Out of memory in malloc >>>
Since typemaps can be named, you can define an exception handler for a specific function as follows :
This will only be applied to the malloc() function returning void *. While you probably wouldn't want to write a different exception handler for every function, it is possible to have a high degree of control if you need it. When typemaps are used, they override any exception handler defined with %except.%typemap(python,except) void *malloc { ... }
As arguments, SWIG_exception() takes an error type code (an integer) and an error message string. The currently supported error types are :// Language independent exception handler %include exception.i %except { try { $function } catch(RangeError) { SWIG_exception(SWIG_ValueError, "Range Error"); } catch(DivisionByZero) { SWIG_exception(SWIG_DivisionByZero, "Division by zero"); } catch(OutOfMemory) { SWIG_exception(SWIG_MemoryError, "Out of memory"); } catch(...) { SWIG_exception(SWIG_RuntimeError,"Unknown exception"); } }
SWIG_MemoryError SWIG_IOError SWIG_RuntimeError SWIG_IndexError SWIG_TypeError SWIG_DivisionByZero SWIG_OverflowError SWIG_SyntaxError SWIG_ValueError SWIG_SystemError SWIG_UnknownError
Since the SWIG_exception() function is defined at the C-level it can be used elsewhere in SWIG. This includes typemaps and helper functions. The exception library provides a language-independent exception handling mechanism, so many of SWIG's library files now rely upon the library as well.
allows you to follow the function calls in order to see where an application might be crashing.%except(tcl) { printf("Entering function : $name\n"); $function printf("Leaving function : $name\n"); }
Exception handlers can also be chained. For example :
Any previously defined exception handler will be inserted in place of the "$except" symbol. As a result, you can attach debugging code to existing handlers if necessary. However, it should be noted that this must be done before any C/C++ declarations are made (as exception handlers are applied immediately to all functions that follow them).%except(tcl) { printf("Entering function : $name\n"); $except printf("Leaving function : $name\n"); }