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.
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'. –
Czy możesz podać szczegółową odpowiedź, więc mogę ją zaakceptować? – choeger
Chętnie to robimy :-) Jest już późno w GMT - 7, to będzie jutro. Pozdrowienia, –