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
2
0
4
;;
- : int = 5
# divide
2
2
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
4
2
3
;;
result = 14
- : unit = ()
# divide_print
2
1
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.