Hi everyone,
I am trying to make a Julia binding of an OCaml library I wrote. To do so, my plan is to first write a C API and then use Julia’s FFI to communicate with the C API.
I found clear instructions in the manual to write a C wrapper for OCaml functions whose input and output types can be naturally represented in C (such as int
, string
or even arrays). For example, if I want to expose fib: int -> int
, I can write the following stub:
int fib(int n) {
static const value * fib_closure = NULL;
if (fib_closure == NULL) fib_closure = caml_named_value("fib");
return Int_val(caml_callback(*fib_closure, Val_int(n)));
}
Also, when manipulating intermediate OCaml values, it is important to use the CAMLlocal
macro so as to keep them from being collected by the GC too early.
My question is about dealing with more complex objects such as recursive ADTs. For example, suppose I have a module with the following signature:
type expr (* voluntarily left abstract *)
val random_expr_of_size: int -> expr
val evaluate: expr -> int
Ideally, I would want to translate this into the following C API:
typedef value* expr;
expr random_expr_of_size(int size);
int evaluate(expr e);
void release(value* obj);
The idea is that the C equivalent of random_expr_of_size
would return a pointer to an opaque OCaml value, which is not to be manipulated directly but rather passed to other functions (such as evaluate
). For this to work though, one must have a way to manually indicate the GC when it is safe to collect this value, which is what the release
function is for. Note that the calls to release
can ultimately be automated on the Julia side by adding hooks to the Julia GC.
However, I am not sure how to implement such a C API. I thought about using caml_register_global_root
and caml_remove_global_root
but:
- I have no idea how efficient it would be.
- I imagine one should be careful that the value returned by
random_expr_of_size
is really a root and no other value points to it. What would be the best way to ensure this? Should I allocate a new shallow copy of the value returned by OCaml (I guess there is no need to copy recursively here) and if yes how can I do that?
I would greatly appreciate your help on this!