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