There is a special syntax for creating and accessing C structs:

struct point {
    int x;
    int y;

/* example functions */
struct point add (struct point a, struct point b) {
  struct point res;
  res.x = a.x + b.x ;
  res.y = a.y + b.y ;
  return res;

void add_ptr(struct point *a, struct point *b, struct point *res){
  res->x = a->x + b->x;
  res->y = a->y + b->y;
type%c point = {
  x: int; (* int here refers to the value, not the type int *)
  y: int;

The above code will be translated to something along the following lines:

type point
let point : point Ctypes.structure Ctypes.typ = Ctypes.structure "point"
let x = Ctypes.field point "x"
let y = Ctypes.field point "y"
let () = Ctypes.seal point

Example usage:

let () =
  let p1 = Ctypes.make point in
  Ctypes.setf p1 x 1;
  Ctypes.setf p1 y 2;
  let p2 = Ctypes.make point in
  Ctypes.setf p2 x 3;
  Ctypes.setf p2 y 4;
  let p3 = add p1 p2 in
  Printf.printf "add (simple): %d;%d\n" (getf p3 x) (getf p3 y);
  let res = Ctypes.make point in
  let () = add_ptr (Ctypes.addr p1) (Ctypes.addr p2) (Ctypes.addr res) in
    "add_ptr (simple): %d;%d\n"
    (Ctypes.getf res x)
    (Ctypes.getf res y)

It’s also possible to convert OCaml records to C structs on the fly by annotating the type declaration with [@@ as_record]:

type%c point = {
  x: int;
  y: int;
} [@@ as_record]
(* [@@ with_record] will create two values: point and point_record *)

external add: point -> point -> point = "add"
external add_ptr: point ptr -> point ptr -> point ptr -> void = "add_ptr"

let () =
  let p1 = { x = 1 ; y = 3} in
  let p2 = { x = 2 ; y = 4} in
  let p3 = add p1 p2 in
  Printf.printf "add (record): %d;%d\n" p3.x p3.y;
  let p1_ptr = allocate point p1 in
  let p2_ptr = allocate point p2 in
  let res_ptr = allocate_n point ~count:1 in
  let () = add_ptr p1_ptr p2_ptr res_ptr in
  let res = !@ res_ptr in
  Printf.printf "add_ptr (record) %d;%d\n" res.x res.y

If the struct contains pointers, you have to be careful that the garbage collector doesn’t free the memory behind your back. The generated code for automatic boxing and unboxing can’t handle such subtle issues for you.

[@@ with_record] is useful, when easier access to the fields is only temporary needed for debugging. Ctypes.coerce can be used to convert between the different pointer representations:

let {x,y} = !@(coerce (ptr point) (ptr point_record) t)


Unions can be declared in a similar way:

typedef union {
  long l;
  double d;
} data;
type%c_union data = {
  l: long;
  d: double;
} [@@ typedef]

Nested Structs and Unions

Structs that contain other structs or unions are best accessed by creating a “flat” OCaml record:

struct s {
  int tag;
  union {
    int i;
    unsigned int u;
  } d;
type%c s = { 
  tag : int;
  d_i : sint [@cname "d.i"];
  d_u : uint [@cname "d.u"];