如何复制OCaml闭包?

时间:2014-04-22 15:23:28

标签: 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>);

但正如名称&#34;关闭&#34;建议,仅仅复制代码位置是不够的。

如何制作<SOURCE>

的(垃圾收集器友好)副本

1 个答案:

答案 0 :(得分:4)

在我们在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);
}

然后一些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 ()

测试运行如下:

$ 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
$ 

我认为重点是(a)代表闭包的OCaml对象已经包含了你需要的东西(某种代码引用和数据)。 (b)你不需要复制它,你只需要确保它不会被垃圾收集。 (c)对caml_register_global_root的调用创建了对闭包的引用,因此GC知道不收集它。

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