Previous Contents Next

Exception handling in C and in Objective CAML

Different languages have different mechanisms for raising and handling exceptions: C relies on setjmp and longjmp, while Objective CAML has built-in constructs for exceptions (try ... with, raise). Of course, these mechanisms are not compatible: they do not keep the same information when setting up a handler. It is extremely hard to safely implement the nesting of exception handlers of different kinds, while ensuring that an exception correctly ``jumps over'' handlers. For this reason, only Objective CAML exceptions can be raised and handled from C; setjmp and longjmp in C cannot be caught from Objective CAML, and must not be used to skip over Objective CAML code.

All functions and macros introduced in this section are defined in the header file fail.h.

Raising a predefined exception

From a C function, it is easy to raise one of the exceptions Failure, Invalid_argument or Not_found from the Pervasives module: just use the following functions.
failwith(s) : raise the exception Failure(s)
invalid_argument(s) : raise the exception Invalid_argument(s)
raise_not_found() : raise the exception Not_found
In the first two cases, s is a C string (char *) that ends up as the argument to the exception raised.

Raising a user-defined exception

A registration mechanism similar to that for closures enables user-defined exceptions to be raised from C. We must first register the exception using the Callback module's register_exception function. Then, from C, we retrieve the exception identifier using the caml_named_value function (see page ??). Finally, we raise the exception, using one of the following functions:
raise_constant(e) raise the exception e with no argument,
raise_with_arg(e,v) raise the exception e with the value v as argument,
raise_with_string(e,s) same, but the argument is taken from the C string s.

Here is an example C function that raises an Objective CAML exception:

#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/fail.h>

value divide (value v1,value v2)
{
CAMLparam2(v1,v2);
if (Long_val(v2) == 0)
raise_with_arg(*caml_named_value("divzero"),v1) ;
CAMLreturn Val_long(Long_val(v1)/Long_val(v2)) ;
}


And here is an Objective CAML transcript showing the use of that C function:


# external divide : int -> int -> int = "divide" ;;
external divide : int -> int -> int = "divide"
# exception Division_zero of int ;;
exception Division_zero of int
# Callback.register_exception "divzero" (Division_zero 0) ;;
- : unit = ()
# divide 20 4 ;;
- : int = 5
# divide 22 0 ;;
Uncaught exception: Division_zero(22)


Catching an exception

In a C function, we cannot catch an exception raised from another C function. However, we can catch Objective CAML exceptions arising from the application of an Objective CAML function (callback). This is achieved via the functions callback_exn, callback2_exn, callback3_exn and callbackN_exn, which are similar to the standard callback functions, except that if the callback raises an exception, this exception is caught and returned as the result of the callback. The result value of the callback_exn functions must be tested with Is_exception_result(v); this predicate returns ``true'' if the result value represents an uncaught exception, and ``false'' otherwise. The macro Extract_exception(v) returns the exception value contained in an exceptional result value.

The C function divide_print below calls the Objective CAML function divide using callback2_exn, and checks whether the result is an exception. If so, it prints a message and raises the exception again; otherwise it prints the result.

#include <stdio.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/callback.h>
#include <caml/fail.h>

value divide_print (value v1,value v2)
{
CAMLparam2(v1,v2) ;
CAMLlocal3(div,dbz,res) ;
div = * caml_named_value("divide") ;
dbz = * caml_named_value("div_by_0") ;
res = callback2_exn (div,v1,v2) ;
if (Is_exception_result(res))
{
value exn=Extract_exception(res);
if (Field(exn,0)==dbz) printf("division by 0\n") ;
else printf("other exception\n");
fflush(stdout);
if (Wosize_val(exn)==1) raise_constant(Field(exn,0)) ;
else raise_with_arg(Field(exn,0),Field(exn,1)) ;
}
printf("result = %d\n",Long_val(res)) ;
fflush(stdout) ;
CAMLreturn Val_unit ;
}

# Callback.register "divide" (/) ;;
- : unit = ()
# Callback.register_exception "div_by_0" Division_by_zero ;;
- : unit = ()
# external divide_print : int -> int -> unit = "divide_print" ;;
external divide_print : int -> int -> unit = "divide_print"
# divide_print 42 3 ;;
result = 14
- : unit = ()
# divide_print 21 0 ;;
division by 0
Uncaught exception: Division_by_zero


As the examples above show, it is possible to raise an exception from C and catch it in Objective CAML, and also to raise an exception from Objective CAML and catch it in C. However, a C program cannot by itself raise and catch an Objective CAML exception.


Previous Contents Next