* catch / reraise exceptions in C, representation of exceptions
@ 2006-05-12 22:42 Hendrik Tews
0 siblings, 0 replies; only message in thread
From: Hendrik Tews @ 2006-05-12 22:42 UTC (permalink / raw)
To: caml-list
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
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2006-05-12 22:42 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-05-12 22:42 catch / reraise exceptions in C, representation of exceptions Hendrik Tews
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox