Version française
Home     About     Download     Resources     Contact us    
Browse thread
catch / reraise exceptions in C, representation of exceptions
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Hendrik Tews <tews@t...>
Subject: catch / reraise exceptions in C, representation of exceptions
Dear all,

I just finished some (C++) code that uses callbacks into ocaml
and catches and reraises some exceptions (raised in the
callbacks). For these matters the documentation ... aehm ... has
some space for improvements. Here are the things that I learned
the hard way (please correct if something is wrong), point 4
contains speculations and point 5 a question:

1. Users of caml_callback_exn must obey an additional garbage
   collector harmony rule:

     An exceptional value obtained from one of the
     caml_callback_exn functions (that is one for which
     Is_exception_result returns true) _must_ _not_ be contained in
     a local or global root when calling one of the allocation
     functions. 

   Because every value must be registered, it follows that you
   either have to clear the exceptional value or replace it with
   something else before the next allocation. Use the following
   pattern, if you are unsure which code may perform allocations:

     result = caml_callback_exn(...);
     is_exception = Is_exception_result(result);
     result = Extract_exception(result);

   Extract_exception returns a non-exceptional value (not to be
   confused with a value that represents an exception). 

   If you look at 

      #define Make_exception_result(v) ((v) | 2)
      #define Is_exception_result(v) (((v) & 3) == 2)
      #define Extract_exception(v) ((v) & ~3)

   in caml/callback.h it is easy to understand why you get
   segfaults sooner or later, if you don't obey the rule obove.


2. Exceptions (ie. results of Extract_exception) are represented
   as blocks of size 1 plus the number of arguments of the
   exception (ie, End_of_file has size 1, exception A of int *
   int * string has size 4, and Match_failure has size 2 because
   it is defined as exception Match_failore of (string * int *
   int)).

   The contents of the first field is the exception id, it
   uniquely identifies the exception. Use the following pattern
   to check if some specific exception occured:

      result = caml_callback_exn(...);
      if(Is_exception_result(result)){
	result = Extract_exception(result);          /* see rule above */
	if(Field(result,0) == *caml_named_value("my_exception_id")){
	  /* exception registered under "my_exception" occured */
	} else {
	  /* some other exception occured */
	}
      }


3. To reraise an unknown exception use caml_raise (with a
   non-exceptional argument). For instance

      result = caml_callback_exn(...);
      if(Is_exception_result(result)){
	result = Extract_exception(result);          /* see rule above */
	if(Field(result,0) == *caml_named_value("Not_found_id")){
	  /* catch Not_found */
          ....
	} 
	else 
          caml_raise(result);               /* reraise other exceptions */
	
      }

   To raise an exception with more than one argument, allocate a
   block of the right size (see point 2), fill the exception id
   in field 0 and the arguments in the remaining fields and use
   caml_raise on this so created value.


4. It seems that an exception id is always a block of size one,
   which contains (the value of) the string of the exception
   name. 

   Is this true? Can one access the exception name that way or
   are programs using this doomed to be broken in the future?


5. Question: Rule 1 says I should CAMLreturn at the end of my
   function. But what if I raise an exception? Can I rely on the
   ocaml runtime to unchain all my local roots, that is, is the
   following safe:


      void f(...){
        CAMLparam(...);
        CAMLlocal(...);
        ....
        caml_raise_with_arg(...);
      }

   There is a CAMLnoreturn, but IMHO it doesn't make sense to put
   it after caml_raise_with_arg.


Bye,

Hendrik