是否可以从OCaml调用C函数,而不包含类型?

时间:2017-10-04 13:56:32

标签: c assembly compilation ocaml ffi

我试图在OCaml中编写一个(非常小,很简单 - 我不知道我在做什么,让它成为真实!)编译器。

我真的很想避免将任何 ISO-C代码检入项目中(尽管很了解C;这里的目标是专门学习和使用OCaml。)根据这一点,我需要为OCaml中的编译语言编写一个“运行时”,将其与主项目分开编译,然后将其与编译器本身的输出相关联。

不幸的是,它看起来像任何外部函数 - 即使那些不接触任何OCaml数据结构/ OCaml堆的函数也应该使用OCaml' C构建宏:

CAMLprim value scheme_entry(value unit) {
    int i;
    i = 42;
    return Val_int(i);
}

如果我自己发出装配说明,这可能不是一个选择。 (至少,直到我学到更多东西!)

是否有任何方式(包括hacky - 这是个人学习项目)从OCaml调用极其简单的函数,如下所示?

_scheme_entry:
    movl $42 %eax
    ret
  

作为参考,我正在通过Ghuloum的IACC工作:http://ell.io/tt$ocameel

2 个答案:

答案 0 :(得分:3)

  

不幸的是,看起来任何外部函数 - 即使不接触任何OCaml数据结构/ OCaml堆的函数 - 都应该使用OCaml的C宏构建:

不,他们不是。如果您的功能根本没有接触OCaml,或者没有分配或使用分配的值,那么您可以直接调用函数,例如,

value scheme_entry(value unit) {
    int i;
    i = 42;
    return Val_int(i);
}

和OCaml方面:

external scheme_entry : unit -> int = "scheme_entry" [@@noalloc]

在OCaml的最新版本中,我们获得了更多的控制权,因此我们可以在没有装箱/拆箱的情况下传递浮点数和更大的整数。请阅读here以获取更多信息。

注意,Val_int只是一个宏,它将C整数向左移一位,并将最低有效位设置为1,即(((unsigned)(x) << 1)) + 1)。因此,如果您不需要将OCaml整数转换为C整数和反之​​,那么您甚至不需要使用此宏,因此运行时可能完全不知道OCaml,例如:

 void runtime_init(void) {
    printf("Hello from runtime");
 }

和OCaml方面:

 val runtime_init : unit -> unit = "runtime_init" [@@noalloc]

注意:OCaml 4.03中添加了[@@noalloc]属性,在该版本之前,您应该使用"noalloc",例如,

 val runtime_init : unit -> unit = "runtime_init" "noalloc"

当然,您非常简单的函数应该遵循适合您的特定体系结构和操作系统的C调用约定。因此,如果你的函数破坏了一些应该保留的寄存器,你应该会遇到麻烦,例如在amd64 ABI上你应该保留rbp,rbx,r12-r15。 OCaml没有特定于此要求的东西,只是一个普通的C调用约定。

答案 1 :(得分:2)

您可能需要考虑使用可通过OPAM获得的ctypes library及相关软件包。

opam install ctypes ctypes-foreign posix-types

示例:

open Foreign
open Ctypes
open Posix_types

let getpid = foreign "getpid" (void @-> returning pid_t)
let () = Printf.printf "%d\n" (Pid.to_int (getpid ()))

(对此进行编译需要包ctypesctypes.foreignposix-types。)