Précédent Index Suivant

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 20 4 ;;
- : int = 5
# divise 22 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 42 3 ;;
resultat = 14
- : unit = ()
# divise_ml 21 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.


Précédent Index Suivant