Author: Christian.Queinnec@lip6.fr

## 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.