2014-04-22 14 views
5

Voglio memorizzare una chiusura OCaml per un utilizzo successivo da una libreria C esterna. Sono in grado di effettuare le seguenti operazioni:Come si copia una chiusura OCaml?

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

Ma come il nome di "chiusura", suggerisce, non è sufficiente per copiare solo la posizione del codice.

Come si effettua una copia (garbage collector) di <SOURCE>?

+1

Quando dici "dopo", intendi più tardi nello stesso processo? Se è così, non è necessario copiare la chiusura (mi sembra). Devi solo assicurarti che non sia GCed prima di volerlo usare. Che cosa stai facendo con 'caml_register_global_root'. –

+0

Potresti per favore fare una risposta dettagliata, quindi posso accettarlo? – choeger

+0

Felice di fare questo :-) È tardi qui a GMT - 7, dovrà essere domani. Saluti, –

risposta

4

Nel nostro lavoro sull'utilizzo di OCaml in iOS, spesso è necessario salvare le chiusure OCaml e chiamarle in un secondo momento (quando si interfacciano alle librerie CocoaTouch). Quindi ho un codice che funziona da anni. Tuttavia, è troppo complicato per fare un buon esempio (ed è scritto nell'Obiettivo C). Ecco un codice che ho appena scritto che cattura l'essenza di ciò che stiamo facendo.

Prima un codice C che consente di risparmiare un certo numero di chiusure di tipo unit -> unit e consente di chiamarle in un secondo momento in base all'indice cronologico. (E 'solo un esempio.)

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

Poi certo codice OCaml che esercita queste funzioni:

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

Un test simile a questa:

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

penso che i punti salienti sono (a) l'oggetto OCaml che rappresenta una chiusura contiene già ciò di cui hai bisogno (un codice di riferimento di qualche tipo e i dati). (b) Non è necessario copiarlo, è sufficiente assicurarsi che non venga raccolta la garbage collection. (c) La chiamata a caml_register_global_root crea un riferimento alla chiusura in modo che il GC sappia di non raccoglierlo.

Spero che questo sia utile. Se qualcuno vede problemi con questo codice, fammelo sapere e sarò più che felice di correggere gli errori. Ma credo sia corretto.