我想存储一个OCaml闭包供以后由外部C库使用.我能够做到以下几点:
<TARGET> = caml_alloc(Wosize_val(<SOURCE>), Tag_val(<SOURCE>));
caml_register_global_root(<TARGET>);
Code_val(<TARGET>) = Code_val(<SOURCE>);
Run Code Online (Sandbox Code Playgroud)
但正如名称"封闭"所暗示的那样,仅仅复制代码位置是不够的.
如何制作(垃圾收集器友好)副本<SOURCE>
?
在我们在 iOS 中使用 OCaml 的工作中,我们经常需要保存 OCaml 闭包并在以后调用它们(当与 CocoaTouch 库交互时)。所以我的代码已经运行了多年。然而,它太复杂了,无法成为一个好的例子(而且它是用 Objective C 编写的)。这是我刚刚编写的一些代码,它们抓住了我们正在做的事情的本质。
首先是一些 C 代码,它保存一些类型的闭包unit -> unit
,并允许您稍后按时间顺序索引调用它们。(这只是一个例子。)
$ cat saveclo.c
#include "caml/mlvalues.h"
#include "caml/memory.h"
#include "caml/callback.h"
static value saved_closures[10];
static int saved_closure_count = 0;
value save_closure(value clo)
{
CAMLparam1(clo);
saved_closures[saved_closure_count] = clo;
caml_register_global_root(&saved_closures[saved_closure_count]);
saved_closure_count++;
CAMLreturn(Val_unit);
}
value call_closure(value index)
{
CAMLparam1(index);
int ix = Int_val(index);
// For simplicity assume closure : unit -> unit
(void) caml_callback(saved_closures[ix], Val_unit);
CAMLreturn(Val_unit);
}
Run Code Online (Sandbox Code Playgroud)
然后是一些执行这些功能的 OCaml 代码:
$ cat clo.ml
external save_closure : (unit -> unit) -> unit = "save_closure"
external call_closure : int -> unit = "call_closure"
let save alist =
let howlong () =
Printf.printf "list length %d\n" (List.length alist)
in
save_closure howlong
let call () =
call_closure 1;
call_closure 0
let () =
save [1;2;3;4;5];
save ['a'; 'b'; 'c'; 'd'; 'e'; 'f'];
Gc.full_major();
call ()
Run Code Online (Sandbox Code Playgroud)
测试运行如下所示:
$ cc -I /usr/local/lib/ocaml -c -o saveclo.o saveclo.c
$ ocamlopt -c clo.ml
$ ocamlopt -o clo clo.cmx saveclo.o
$ ./clo
list length 6
list length 5
$
Run Code Online (Sandbox Code Playgroud)
我认为要点是(a)表示闭包的 OCaml 对象已经包含您需要的内容(某种代码引用和数据)。(b) 你不需要复制它,你只需要确保它不会被垃圾收集。(c) 调用caml_register_global_root
创建对闭包的引用,以便 GC 知道不要收集它。
我希望这是有帮助的。如果有人发现此代码有问题,请告诉我,我将非常乐意纠正错误。但我相信这是正确的。