<div dir="ltr">Thank you, Jeremy. This is exactly what I needed.<div><br></div><div>Thank you as well for Ctypes, from my brief use so far it seems great.</div><div><br></div><div>Travis</div></div><div class="gmail_extra">
<br><br><div class="gmail_quote">On Tue, Dec 17, 2013 at 5:33 PM, Jeremy Yallop <span dir="ltr"><<a href="mailto:yallop@gmail.com" target="_blank">yallop@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="im">On 17 December 2013 04:46, Travis Brady <<a href="mailto:travis.brady@gmail.com">travis.brady@gmail.com</a>> wrote:<br>
> I'm wrapping a simple library that contains a few functions that call malloc<br>
> or realloc internally to allocate a few struct members and I'm wondering how<br>
> to make those behave with Ctypes and the GC.<br>
><br>
> The library exposes structname_free functions where applicable, but I'm not<br>
> certain how to tell Ctypes how to use them or if I even need to at all.<br>
<br>
</div>There are several possible approaches to consider. One approach is to<br>
simply write C-style code in OCaml, binding the structname_free<br>
functions and explicitly calling them when you know that it's safe to<br>
do so. This will probably work best if the lifetime of the struct is<br>
easy to predict -- for example, if it's associated with a file<br>
descriptor or other resource that also needs to be explicitly<br>
released.<br>
<br>
Another approach is to register the destructor functions with the GC<br>
so that they're called automatically when ctypes no longer has a<br>
reference to the memory. This approach should work well where you're<br>
allocating the memory from ctypes, since the hooks for registering<br>
destructor functions are currently associated with the ctypes<br>
allocation functions such as make<br>
(<a href="http://ocamllabs.github.io/ocaml-ctypes/Ctypes.html#VALmake" target="_blank">http://ocamllabs.github.io/ocaml-ctypes/Ctypes.html#VALmake</a>).<br>
<br>
Here's a simple example with a struct whose fields point to memory<br>
dynamically allocated by C code. The unmanaged_person function shows<br>
the C-style approach, with explicit deallocation; the managed_person<br>
function shows the GC-based approach, using the finaliser argument to<br>
the make function to register the destructor:<br>
<br>
$ cat example_stubs.c<br>
#include <stdlib.h><br>
#include <string.h><br>
#include <stdio.h><br>
<br>
struct person { char *name; int age; };<br>
<br>
void initialize_person(struct person *p, const char *name, int age)<br>
{<br>
p->name = malloc(strlen(name) + 1);<br>
strcpy(p->name, name);<br>
p->age = age;<br>
}<br>
<br>
void destroy_person(struct person *p)<br>
{<br>
printf("goodbye %s (age %d)\n", p->name, p->age);<br>
free(p->name);<br>
}<br>
$ cat <a href="http://example.ml" target="_blank">example.ml</a><br>
open Ctypes<br>
open Foreign<br>
<br>
type person<br>
let person : person structure typ = structure "person"<br>
let name = field person "name" string<br>
let age = field person "age" int<br>
let () = seal person<br>
<br>
let initialize_person = foreign "initialize_person"<br>
(ptr person @-> string @-> int @-> returning void)<br>
<br>
let destroy_person = foreign "destroy_person"<br>
(ptr person @-> returning void)<br>
<br>
(* Allocate a `person' value, registering the destructor function<br>
with the GC *)<br>
let managed_person ~name ~age =<br>
let p = make ~finalise:(fun p -> destroy_person (addr p)) person in<br>
initialize_person (addr p) name age;<br>
p<br>
<br>
(* Allocate a `person' value that must be explicitly deallocated *)<br>
let unmanaged_person ~name ~age =<br>
let p = make person in<br>
initialize_person (addr p) name age;<br>
p<br>
$ ocamlfind ocamlc -c -package ctypes example_stubs.c <a href="http://example.ml" target="_blank">example.ml</a><br>
$ ocamlmklib -o example example_stubs.o example.cmo<br>
$ ocaml<br>
OCaml version 4.01.0<br>
<br>
# #use "topfind";;<br>
[...]<br>
# #require "ctypes.foreign";;<br>
[...]<br>
# #load "example.cma";;<br>
# open Example;;<br>
# let mcc = unmanaged_person "Mrs McCave" 40;;<br>
val mcc : (Example.person, [ `Struct ]) Ctypes.structured = <abstr><br>
# let () = for i = 1 to 23 do ignore (managed_person ~name:"Dave"<br>
~age:i) done;;<br>
# Gc.full_major ();;<br>
goodbye Dave (age 23)<br>
goodbye Dave (age 22)<br>
goodbye Dave (age 21)<br>
goodbye Dave (age 20)<br>
goodbye Dave (age 19)<br>
goodbye Dave (age 18)<br>
goodbye Dave (age 17)<br>
goodbye Dave (age 16)<br>
goodbye Dave (age 15)<br>
goodbye Dave (age 14)<br>
goodbye Dave (age 13)<br>
goodbye Dave (age 12)<br>
goodbye Dave (age 11)<br>
goodbye Dave (age 10)<br>
goodbye Dave (age 9)<br>
goodbye Dave (age 8)<br>
goodbye Dave (age 7)<br>
goodbye Dave (age 6)<br>
goodbye Dave (age 5)<br>
goodbye Dave (age 4)<br>
goodbye Dave (age 3)<br>
goodbye Dave (age 2)<br>
goodbye Dave (age 1)<br>
- : unit = ()<br>
# destroy_person (Ctypes.addr mcc);;<br>
goodbye Mrs McCave (age 40)<br>
- : unit = ()<br>
</blockquote></div><br></div>