如何复制OCaml闭包?

cho*_*ger 5 c closures ocaml

我想存储一个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>

Jef*_*eld 5

在我们在 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 知道不要收集它。

我希望这是有帮助的。如果有人发现此代码有问题,请告诉我,我将非常乐意纠正错误。但我相信这是正确的。