OCaml Ctypes 和分配一个指向类型的指针

OCaml Ctypes and allocating a pointer to a type

我正在尝试从 OCaml 调用一些 C 代码,我需要在其中提供指向我的类型的分配指针 yaml_parser_t。但我不确定我应该如何分配一个有效的指针。示例代码如下。

理想情况下,我也不想为 yaml_parser_t 提供具体的实现,因为我不需要检查它的内部结构,只需将它传入和传出各种函数即可。我最初是在遵循 Real World OCaml 中的 time_t 示例,但他们似乎正在使用 time 函数来分配我这里没有的函数。

对于混乱的解释,我们深表歉意。

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;;

为了分配一些东西,您需要知道它的大小。在 libyaml 库中,yaml_parser_t 类型不是不透明的,因此使用这种类型最正确的方法是在 ctypes 中将其声明为结构并描述其所有字段。在这种情况下,您可以只使用 allocate 函数来创建值。但是,如果你拒绝这样做,我会理解你。 yaml_parser_t 结构巨大,生命太短暂。由于无法在运行时发现结构的大小,因此您需要编写 C 存根函数,或者只是在您的库中对其进行硬编码。后者并没有那么糟糕,正如人们可能认为的那样,因为大小只应在主要版本更改时更改,因为 yaml_parser_t 已明确设为非不透明,并被视为界面的一部分。

为抽象值分配数据

ctypes 中有两个函数可以分配内存,即allocateallocate_n。前者需要分配值的实例。由于我们的类型是抽象的,我们将使用后者,因为它不需要我们提供值。

首先我们需要描述一个抽象类型。我们只需要提供三个值:名称、大小和对齐方式。名字很简单,可以是任意字符串。只有 C 编译器才能确定大小和对齐方式。最简单的方法是编写一个小程序,使用 sizeof__alignof__ 编译时指令打印它们。然后将输出复制粘贴到您的 ml 代码中。如果你发现这个解决方案很脏,那么你可以编写两个原始的 c 函数,在运行时将 return 这个值。因此,假设您已经检索了这些值,那么我们现在可以为 yaml_parser_t:

创建一个类型
let size = 100
let alignment = 0

let yaml_parser_t : unit abstract typ =
  abstract ~name:"yaml_parser_t" ~size ~alignment

现在可以使用yaml_parser_t分配内存了:

let allocate_yaml_parser () : unit abstract ptr =
  allocate_n yaml_parser_t ~count:1

然后你可以尝试分配它:

# let p = allocate_yaml_parser ();;
val p : unit Ctypes.abstract Ctypes.ptr = (yaml_parser_t*) 0x10156d0

接下来,您可以将其转换为 void 或其他类型并将其传递给您的存根。

P.S。 libyaml 接口很奇怪,这是问题的根源。 yaml_parser_t 类型应该是不透明的,并且应该提供一个创建它的函数。但是,不幸的是,我们拥有我们所拥有的。