package ctypes

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file cstubs.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
(*
 * Copyright (c) 2014 Jeremy Yallop.
 *
 * This file is distributed under the terms of the MIT License.
 * See the file LICENSE for details.
 *)

(* Cstubs public interface. *)

[@@@warning "-27-32"]

module type FOREIGN = Ctypes.FOREIGN

module type FOREIGN' = FOREIGN with type 'a result = unit

module type BINDINGS = functor (F : FOREIGN') -> sig end

type concurrency_policy = [ `Sequential | `Lwt_jobs | `Lwt_preemptive | `Unlocked ]

type errno_policy = [ `Ignore_errno | `Return_errno ]

let gen_c ~concurrency ~errno prefix fmt : (module FOREIGN') =
  (module
   struct
     let counter = ref 0
     let var prefix name = incr counter;
       Printf.sprintf "%s_%d_%s" prefix !counter name
     type 'a fn = 'a Ctypes.fn
     type 'a return = 'a
     type 'a result = unit
     let foreign cname fn =
       Cstubs_generate_c.fn ~concurrency ~errno
         ~cname ~stub_name:(var prefix cname) fmt fn
     let foreign_value cname typ =
       Cstubs_generate_c.value ~cname ~stub_name:(var prefix cname) fmt typ
     let returning = Ctypes.returning
     let (@->) = Ctypes.(@->)
   end)

type bind = Bind : string * string * ('a -> 'b) Ctypes.fn -> bind
type val_bind = Val_bind : string * string * 'a Ctypes.typ -> val_bind

let write_return :
  concurrency:concurrency_policy -> errno:errno_policy ->
  Format.formatter -> unit =
  fun ~concurrency ~errno fmt -> match concurrency, errno with
      (`Sequential|`Unlocked), `Ignore_errno -> Format.fprintf fmt "type 'a return = 'a@\n"
    | (`Sequential|`Unlocked), `Return_errno -> Format.fprintf fmt "type 'a return = 'a * Signed.sint@\n"
    | (`Lwt_jobs|`Lwt_preemptive), `Ignore_errno ->
      begin
        Format.fprintf fmt "type 'a return = { lwt: 'a Lwt.t }@\n";
        Format.fprintf fmt "let box_lwt lwt = {lwt}@\n";
      end
    | (`Lwt_jobs|`Lwt_preemptive), `Return_errno ->
      begin
        Format.fprintf fmt "type 'a return = { lwt: ('a * Signed.sint) Lwt.t }@\n";
        Format.fprintf fmt "let box_lwt lwt = {lwt}@\n";
      end

let write_fn ~concurrency ~errno fmt =
  begin
    Format.fprintf fmt "type 'a fn =@\n";
    Format.fprintf fmt " | Returns  : 'a CI.typ   -> 'a return fn@\n";
    Format.fprintf fmt " | Function : 'a CI.typ * 'b fn  -> ('a -> 'b) fn@\n"
  end

let write_map_result ~concurrency ~errno fmt =
  match concurrency, errno with
    (`Sequential|`Unlocked), `Ignore_errno ->
    Format.fprintf fmt "let map_result f x = f x@\n"
  | (`Sequential|`Unlocked), `Return_errno ->
    Format.fprintf fmt "let map_result f (x, y) = (f x, y)@\n"
  | (`Lwt_jobs|`Lwt_preemptive), `Ignore_errno ->
    Format.fprintf fmt "let map_result f x = Lwt.map f x@\n"
  | (`Lwt_jobs|`Lwt_preemptive), `Return_errno ->
    Format.fprintf fmt "let map_result f v = Lwt.map (fun (x, y) -> (f x, y)) v@\n"

let write_foreign ~concurrency ~errno fmt bindings val_bindings =
  Format.fprintf fmt
    "type 'a result = 'a@\n";
  write_return ~concurrency ~errno fmt;
  write_fn ~concurrency ~errno fmt;
  write_map_result ~concurrency ~errno fmt;
  Format.fprintf fmt
    "let returning t = Returns t@\n";
  Format.fprintf fmt
    "let (@@->) f p = Function (f, p)@\n";
  Format.fprintf fmt
    "let foreign : type a b. string -> (a -> b) fn -> (a -> b) =@\n";
  Format.fprintf fmt
    "  fun name t -> match t, name with@\n@[<v>";
  ListLabels.iter bindings
    ~f:(fun (Bind (stub_name, external_name, fn)) ->
      Cstubs_generate_ml.case ~concurrency ~errno ~stub_name ~external_name fmt fn);
  Format.fprintf fmt "@[<hov 2>@[|@ _,@ s@ ->@]@ ";
  Format.fprintf fmt
    " @[Printf.ksprintf@ failwith@ \"No match for %%s\" s@]@]@]@.@\n";
  Format.fprintf fmt
    "@\n";
  Format.fprintf fmt
    "let foreign_value : type a. string -> a Ctypes.typ -> a Ctypes.ptr =@\n";
  Format.fprintf fmt
    "  fun name t -> match t, name with@\n@[<v>";
  ListLabels.iter val_bindings
    ~f:(fun (Val_bind (stub_name, external_name, typ)) ->
      Cstubs_generate_ml.val_case ~stub_name ~external_name fmt typ);
  Format.fprintf fmt "@[<hov 2>@[|@ _,@ s@ ->@]@ ";
  Format.fprintf fmt
    " @[Printf.ksprintf@ failwith@ \"No match for %%s\" s@]@]@]@.@\n"

let gen_ml ~concurrency ~errno prefix fmt : (module FOREIGN') * (unit -> unit) =
  let bindings = ref []
  and val_bindings = ref []
  and counter = ref 0 in
  let var prefix name = incr counter;
    Printf.sprintf "%s_%d_%s" prefix !counter name in
  (module
   struct
     type 'a fn = 'a Ctypes.fn
     type 'a return = 'a
     let (@->) = Ctypes.(@->)
     let returning = Ctypes.returning

     type 'a result = unit
     let foreign cname fn =
       let name = var prefix cname in
       bindings := Bind (cname, name, fn) :: !bindings;
       Cstubs_generate_ml.extern ~concurrency ~errno
         ~stub_name:name ~external_name:name fmt fn
     let foreign_value cname typ =
       let name = var prefix cname in
       Cstubs_generate_ml.extern ~concurrency:`Sequential ~errno:`Ignore_errno
         ~stub_name:name ~external_name:name fmt
         Ctypes.(void @-> returning (ptr void));
       val_bindings := Val_bind (cname, name, typ) :: !val_bindings
     let returning = Ctypes.returning
     let (@->) = Ctypes.(@->)
   end),
  fun () ->
    write_foreign ~concurrency ~errno fmt !bindings !val_bindings

let sequential = `Sequential
let lwt_jobs = `Lwt_jobs
let lwt_preemptive = `Lwt_preemptive
let ignore_errno = `Ignore_errno
let return_errno = `Return_errno
let unlocked = `Unlocked

let concurrency_headers = function
    `Sequential -> []
  | `Lwt_jobs | `Lwt_preemptive ->   ["\"lwt_unix.h\"";  "<caml/memory.h>"]
  | `Unlocked -> ["<caml/threads.h>"]

let errno_headers = function
    `Ignore_errno -> []
  | `Return_errno -> ["<errno.h>"]

let headers : concurrency_policy -> errno_policy -> string list =
  fun concurrency errno ->
    ["\"ctypes_cstubs_internals.h\""] @
    errno_headers errno @
    concurrency_headers concurrency

let write_c ?(concurrency=`Sequential) ?(errno=`Ignore_errno)
    fmt ~prefix (module B : BINDINGS) =
  List.iter (Format.fprintf fmt "#include %s@\n") (headers concurrency errno);
  let module M = B((val gen_c ~concurrency ~errno prefix fmt)) in ()

let write_ml ?(concurrency=`Sequential) ?(errno=`Ignore_errno)
    fmt ~prefix (module B : BINDINGS) =
  let foreign, finally = gen_ml ~concurrency ~errno prefix fmt in
  let () = Format.fprintf fmt "module CI = Cstubs_internals@\n@\n" in
  let module M = B((val foreign)) in
  finally ()

module Types = Cstubs_structs
OCaml

Innovation. Community. Security.