2014-04-22 18 views
5

Chcę przechowywać zamknięcie OCaml do późniejszego wykorzystania przez zewnętrzną bibliotekę C. Jestem w stanie wykonać następujące czynności:Jak skopiować zamknięcie OCaml?

<TARGET> = caml_alloc(Wosize_val(<SOURCE>), Tag_val(<SOURCE>)); 
caml_register_global_root(<TARGET>); 
Code_val(<TARGET>) = Code_val(<SOURCE>); 

Ale jak sama nazwa „zamknięcie” sugeruje, że nie wystarczy skopiować tylko lokalizację kodu.

Jak zrobić kopię przyjazną dla śmieciarek pod numerem <SOURCE>?

+1

Kiedy mówisz "później", masz na myśli później w tym samym procesie? Jeśli tak, nie musisz kopiować zamknięcia (wydaje mi się). Musisz tylko upewnić się, że nie jest GCed przed użyciem. Co robisz z 'caml_register_global_root'. –

+0

Czy możesz podać szczegółową odpowiedź, więc mogę ją zaakceptować? – choeger

+0

Chętnie to robimy :-) Jest już późno w GMT - 7, to będzie jutro. Pozdrowienia, –

Odpowiedz

4

W naszych pracach nad używaniem OCaml w iOS, często musimy zapisywać zamknięte OCaml i wywoływać je później (podczas łączenia się z bibliotekami CocoaTouch). Mam więc kod, który działa od lat. Jednak zbyt skomplikowany jest dobry przykład (i napisany w Celu C). Oto napisany przeze mnie kod, który oddaje istotę tego, co robimy.

Najpierw trochę kodu C, który zapisuje pewną liczbę zamknięć typu unit -> unit i umożliwia późniejsze wywoływanie ich według indeksu chronologicznego. (Jest to tylko przykład.)

$ 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); 
} 

Potem jakiś kod OCaml że wykonuje te funkcje:

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

Trasa Test wygląda następująco:

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

myślę najistotniejsze punkty są (a) obiekt OCaml reprezentujący zamknięcie zawiera już to, czego potrzebujesz (kod jakiegoś rodzaju i dane). (b) Nie musisz go kopiować, musisz tylko upewnić się, że nie zbiera się śmieci. (c) Połączenie z numerem caml_register_global_root tworzy odniesienie do zamknięcia, więc GC wie, że go nie zbiera.

Mam nadzieję, że to będzie pomocne. Jeśli ktoś napotka problemy z tym kodem, daj mi znać, a ja z przyjemnością poprawię błędy. Ale uważam, że to prawda.