[ocaml-ctypes] OCaml Ctypes and allocating a pointer to a type

Jeremy Yallop yallop at gmail.com
Sun Jan 4 00:19:33 GMT 2015


On 2 January 2015 at 23:01, Tim McGilchrist <timmcgil at gmail.com> wrote:
> I'm trying to call some C code from OCaml where I need to supply an
> allocated pointer to my type yaml_parser_t. But I'm not sure how I should be
> allocating a valid pointer. The example code is below.
>
> Ideally I'd like to not have to supply a concrete implementation for
> yaml_parser_t as well, since I don't need to inspect it's internals, just
> pass it into and out of various functions.

There are a few ways to approach this.  If you have some way of
finding out the size of a yaml_parser_t (e.g. via a configure script)
then you can just allocate a buffer of that size and use it at an
appropriate type.  For example, you might describe a parser as an
struct type without actually specifying any fields:

  type yaml_parser_t = [`yaml_parser_s] structure
  let parser_t : yaml_parser_t typ = structure "yaml_parser_s"

and then allocate parser_t values using something like the following:

  let make_parser () =
    coerce (ptr char) (ptr parser_t) (allocate_n ~count:parser_size char)

(A couple of things that you might wonder about: allocate_n returns
properly aligned memory, just like C's malloc(), so this is safe in
terms of alignment; the buffer returned by allocate_n will be released
automatically when you no longer have a reference to it).

You can use the definition above in your "foreign" bindings, of course:

   let init = foreign "yaml_parser_initialize" (ptr parser_t @-> returning int)

Another possibility, if you're happy to work with the bleeding edge
(i.e. unreleased Ctypes from GitHub) is to use "struct stubs", which
will retrieve the size information for you without requiring you to
describe parts of the structure that you're not going to use.  The
functions for describing structures are essentially the same as what's
described in Real World OCaml, but the build process is a little more
complex.

Here's an example of the struct stubs approach.  The main thing you
need to do is describe the bits of the structure that you're
interested in and put the description inside a functor with argument
type Cstubs.Types.TYPE.

  module Bindings(S : Cstubs.Types.TYPE) =
  struct
    open S
    let yaml_parser_s : [`yaml_parser_s] structure typ =
      structure "yaml_parser_s"
    let () = seal yaml_parser_s
  end

You can then pass the functor to the write_c function to generate some
C code which will determine the layout of the structure:

   (* write the C code to stdout with the appropriate header: *)
   print_endline "#include <yaml.h>";
   Cstubs.Types.write_c Format.std_formatter (module Bindings)

Compiling and running the C code will generate an OCaml module with
the size and alignment information inlined:

   ocamlfind ocamlc -c -package ctypes.stubs generated_c.c
   gcc generated_c.o -o generated_c
   ./generated_c > generated_module.ml
   ocamlfind ocamlc -c -package ctypes.stubs generated_module.ml

Finally you can apply the Bindings functor to the generated module to
bring the layout information into your program:

    # #load "generated_module.cmo";;
    # include Bindings(Generated_module);;
    val yaml_parser_s : [ `yaml_parser_s ] structure typ =
      Static.Struct
       {Static.tag = "yaml_parser_s";
        spec = Static.Complete {Static.size = 480; align = 8}; fields = []}

and you can immediately query the layout:

    # sizeof yaml_parser_s;;
    - : int = 480

and allocate objects of the yaml_parser_s type:

    # make yaml_parser_s;;
    - : ([ `yaml_parser_s ], [ `Struct ]) structured =
    {Static.structured = Static.CPointer <abstr>}

I hope that helps!

Jeremy.


More information about the Ctypes mailing list