OCaml Ctypes and pointer to type allocation
I am trying to call C code from OCaml where I need to cast an assigned pointer to my type yaml_parser_t
. But I'm not sure how I should assign a valid pointer. Below is a sample code.
Ideally, I would not want to provide a specific implementation for yaml_parser_t
, since I don't need to check its internals, just pass it to various functions. I originally followed the time_t example from Real World OCaml , but they seem to use a function time
to highlight which I don't have here.
Sorry for the confusing explanation.
open PosixTypes;;
open Ctypes;;
open Foreign;;
type yaml_parser_t = unit;;
let yaml_parser_t : yaml_parser_t typ = void;;
(* To get it working in utop, specify the name of the library *)
let libyaml = Dl.(dlopen ~filename:"libyaml.dylib" ~flags:[RTLD_NOW]);;
let init = foreign "yaml_parser_initialize" (ptr yaml_parser_t @-> returning int);;
let make =
let p_ptr = allocate yaml_parser_t (from_voidp yaml_parser) in
let _ = init p_ptr in
p_ptr;;
source to share
To make something stand out, you need to know its size. In the library, the libyaml
type is yaml_parser_t
not opaque, so the most correct way to work with such a type would be to declare it in ctypes as a struct and describe all its fields. In this case, you can simply use a function allocate
to create the value. But I will understand you if you refuse to do it. The structure yaml_parser_t
is huge and life is too short. Since there is no way to know the size of the structure at runtime, you either need to write the function with stub or just write it in your library. The latter is not as bad as one might think, since the size should only change when major versions change, since it is yaml_parser_t
explicitly made opaque and considered part of the interface.
Highlighting data for abstract values
There are two functions in ctypes that allow you to allocate memory, namely allocate
and allocate_n
. The first requires an instance of the allocated value. Since our type is abstract, we will use the latter since it does not require a value from us.
First, we need to define an abstract type. We only need to provide three values: name, size and alignment. The name is easy, it can be an arbitrary string. The size and alignment are only known for sure for the C compiler. The easiest way is to write a small program that prints them using time sizeof
and instructions __alignof__
. And then copy the file into your code ml
. If you find this solution messy, you can write two primitive c functions that return these values ββat runtime. So, let's say you've restored these values, then we can now create a type for yaml_parser_t
:
let size = 100
let alignment = 0
let yaml_parser_t : unit abstract typ =
abstract ~name:"yaml_parser_t" ~size ~alignment
Now you can use yaml_parser_t
to allocate memory:
let allocate_yaml_parser () : unit abstract ptr =
allocate_n yaml_parser_t ~count:1
And then you can try to highlight it:
# let p = allocate_yaml_parser ();;
val p : unit Ctypes.abstract Ctypes.ptr = (yaml_parser_t*) 0x10156d0
Then you can cast it to empty or some other type and pass it to your stubs.
PS The interface is libyaml
rather strange, that is, the root of the problem. The type yaml_parser_t
must be opaque and given a function that creates it. But unfortunately, we have what we have.
source to share