Gestion des exceptions en C et en Objective CAML
Les mécanismes de rupture et reprise de calcul varient selon les
langages : C utilise le couple setjmp-longjmp) et les
exceptions Objective CAML, la construction (try .. with,
raise). Ces mécanismes ne sont, bien sûr, pas
compatibles. C'est-à-dire qu'ils ne conservent pas les mêmes
informations à la pose d'un récupérateur. Le fait de pouvoir imbriquer
des récupérateurs d'exceptions de nature différente et d'en
outrepasser certains pose de réelles difficultés d'implantation pour
garantir la sûreté de leur utilisation. C'est pour cela que seules
les exceptions Objective CAML sont traitées. Néanmoins rien n'empêche de les
déclencher ou de les récupérer en C.
Toutes les macros et les fonctions utilisées dans cette partie sont
définies dans le fichier fail.h.
Déclencher une exception prédéfinie
Il est facilement possible de déclencher, depuis une fonction C, les
exceptions Failure ou Invalid_argument du module
Pervasives en utilisant les fonctions suivantes :
failwith(s) |
: |
déclenche l'exception Failure(s) |
invalid_argument(s) |
: |
déclenche l'exception
Invalid_argument(s). |
Dans les deux cas, s est une chaîne de caractères C (char *).
Déclencher une exception quelconque
Le mécanisme pour lever une exception quelconque définie en
Objective CAML est semblable à celui utilisé pour appliquer une
fermeture. Il faut dans un premier temps enregistrer l'exception par
la fonction register_exception du module
Callback. On récupère ensuite la valeur avec la fonction
caml_named_value (voir page
??). On déclenche l'exception avec l'une des
fonctions suivantes :
raise_constant(e) |
lève l'exception e, |
raise_with_arg(e,v) |
lève l'exception e avec v pour argument, |
raise_with_string(e,s) |
idem, mais la valeur est une copie
de la chaîne s. |
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/fail.h>
value division (value v1,value v2)
{
CAMLparam2(v1,v2);
if (!Long_val(v2))
raise_with_arg(*caml_named_value("exception"),v1) ;
CAMLreturn Val_long(Long_val(v1)/Long_val(v2)) ;
}
# external
divise
:
int
->
int
->
int
=
"division"
;;
external divise : int -> int -> int = "division"
# exception
Division_par_zero
of
int
;;
exception Division_par_zero of int
# Callback.register_exception
"exception"
(Division_par_zero
0
)
;;
- : unit = ()
# divise
2
0
4
;;
- : int = 5
# divise
2
2
0
;;
Uncaught exception: Division_par_zero(22)
Rattraper une exception
Il est possible de rattraper une exception depuis une fonction C, mais
seulement si elle a été déclenchée par l'application d'une fermeture
venue d'Objective CAML et non directement depuis une fonction C.
Les macros callback_exn, callback2_exn, callback3_exn et callbackN_exn fonctionnent comme les macros
callback simples, sauf que si l'application a déclenché une
exception, celle-ci est rendue comme résultat.
Le résultat d'un callback_exn peut être testé par le
prédicat Is_exception_result(v) qui retourne 1 si la valeur
provient du déclenchement d'une exception et 0 sinon. On retrouve le constructeur de l'exception par appel à la fonction Extract_exception(v).
La fonction C divise_affiche appelle une fonction
divise et teste si le retour est une exception, en utilisant
l'appel callback2_exn. Si c'est le cas, elle affiche une trace
et la redéclenche, sinon elle affiche le résultat.
#include <stdio.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/callback.h>
#include <caml/fail.h>
value divise_affiche (value v1,value v2)
{
CAMLparam2(v1,v2) ;
CAMLlocal3(div,dbz,res) ;
div = * caml_named_value("divise") ;
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 par 0\n") ;
else printf("autre 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("resultat = %d\n",Long_val(res)) ;
fflush(stdout) ;
CAMLreturn Val_unit ;
}
# Callback.register
"divise"
(/
)
;;
- : unit = ()
# Callback.register_exception
"div_by_0"
Division_by_zero
;;
- : unit = ()
# external
divise_ml
:
int
->
int
->
unit
=
"divise_affiche"
;;
external divise_ml : int -> int -> unit = "divise_affiche"
# divise_ml
4
2
3
;;
resultat = 14
- : unit = ()
# divise_ml
2
1
0
;;
division par 0
Uncaught exception: Division_by_zero
Il est donc possible de déclencher une exception en C et de la
rattraper en Objective CAML; de même, il est possible de déclencher une
exception depuis Objective CAML et la rattraper en C. En revanche, un
programme C ne peut pas à lui seul déclencher et
rattraper une exception d'Objective CAML.