我在定义接受 uint64_t
和 char*
的 C void 函数的签名时遇到问题。我尝试了 int64 -> string -> _
。
我也不知道如何一起编译我的C++文件(带有C接口(interface))
events.ml
open Lwt.Infix
external call: int64 -> string -> _ = "function_pointer_caller"
let begin_event pointer =
Lwt_unix.sleep 5.0 >>= fun () ->
call pointer "message"
let () = Callback.register "begin_event" begin_event
接口(interface).c:
#include <stdio.h>
#include <string.h>
#include <caml/mlvalues.h>
#include <caml/callback.h>
#include <caml/alloc.h>
#include <caml/bigarray.h>
extern void register_function_callback();
void print_from_event(char* message) {
printf("OCaml event: %s\n", message);
}
void function_pointer_caller(uint64_t pointer, char* message)
{
void (*f)(char *);
f = pointer;
}
void register_function_callback() {
static const value *begin_event_closure = NULL;
if (begin_event_closure == NULL)
{
begin_event_closure = caml_named_value("begin_event");
if (begin_event_closure == NULL)
{
printf("couldn't find OCaml function\n");
exit(1);
}
}
uint64_t pointer = (uint64_t) &function_pointer_caller;
caml_callback(*begin_event_closure, (int64_t) &pointer);
}
main.cc
#include <stdio.h>
#include <caml/callback.h>
extern "C" void register_function_callback();
int main(int argc, char **argv)
{
caml_startup(argv);
register_function_callback();
while (true)
{
}
return 0;
}
我认为没有办法将.cc 与.ml 一起编译,因为.cc 不一定有C 接口(interface)。也许可以将 .ml 编译为 .so 对象并使用 C 接口(interface)将其链接到 .cc ?
无论如何,我确实将 interface.cc
更改为 interface.c
并将 interface.c
添加到 ocamlopt
> 命令:
ocamlfind ocamlopt -o s -linkpkg -package lwt.unix -thread event_emitter.ml interface.c
g++ -o event_emitter_program -I $(ocamlopt -where) \
s.o interface.o main.cc event_emitter.o $(ocamlopt -where)/libasmrun.a -ldl
第一个命令编译正常,但 g++ 给出
event_emitter.o: In function `camlEvent_emitter__begin_event_90':
:(.text+0x3f): undefined reference to `camlLwt_unix__sleep_695'
:(.text+0x4c): undefined reference to `camlLwt__bind_1276'
collect2: error: ld returned 1 exit status
请注意,我插入了上一个命令 (ocamlopt
) 中的 interface.o
并在 g++ 中链接,因为 ocamlopt
实际上传递了 C 文件到 C 编译器。
我不知道为什么它会提示 Lwt_unix
的事情,因为我已经在 ocamlopt
中使用它们进行了编译。
最佳答案
ocamlopt
实用程序不仅仅是链接指定的编译单元和库,它还嵌入了架构启动代码,该代码未包含在 asmrun
中图书馆。可以使用 -output-obj
获取此代码,但我发现它有点容易出错并且难以维护,因为它并不是真正可组合的1。
因此,最好依靠 ocamlopt 来构建最终的二进制文件,该二进制文件将由用不同语言编写的模块组成。每个单元都将使用适合其编写语言的工具来构建。让我们构建 events.ml
编译单元:
ocamlfind ocamlopt -c -package lwt.unix events.ml
现在,让我们构建界面(我们可以在这里使用 cc
,但使用 ocamlopt 将为我们省去一些提供包含位置的麻烦)
ocamlfind ocamlopt -c interface.c
现在,让我们构建 C++ 部分(但首先修复它并使用 caml_main
而不是 caml_startup
,因为我们需要 native 运行时)。
g++ -c main.cc -o main.o -I `ocamlc -where`
现在,当我们拥有所有单元后,我们就可以执行最终的链接命令了:
ocamlfind ocamlopt events.cmx interface.o main.o -package lwt.unix -package lwt -thread -linkpkg -o emitter
瞧,我们可以使用 ./emitter
运行我们的程序。
1) 对多个编译单元使用此选项可能很容易导致符号冲突。
关于c++ - 如何编译相互依赖的 OCaml 和 C/C++ 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61602290/