1

我在定义接受 anuint64_t和 a的 C void 函数的签名时遇到问题char*。我试过了int64 -> string -> _

我也不知道如何一起编译我的 C++ 文件(带有 C 接口)

事件.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

接口.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);
}

主文件

#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 接口。也许可以将 .ml 编译为 .so 对象并使用 C 接口将其链接到 .cc ?

无论如何,我确实更改interface.ccinterface.c添加interface.cocamlopt命令中:

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

请注意,我插入了interface.o上一个命令 ( ocamlopt) 并在 g++ 中链接,因为ocamlopt实际上将 C 文件传递​​给 C 编译器。

我不知道为什么它会抱怨Lwt_unix,因为我已经在ocamlopt.

4

2 回答 2

2

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因为我们想要本机运行时)。

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)对多个编译单元使用该选项可能容易导致符号冲突。

于 2020-05-11T17:38:34.127 回答
1

您应该更加小心“警告”消息,而不是依赖将构建过程绑定到您的配置的东西。请避免使用根控制台!

[WARNING] Running as root is not recommended

在根环境中,库的路径可能很复杂。

根据您的 pastebin,我看到链接器无法找到 lwt_unix_stubs 库。请不要混合使用“.a”和“.so”库(/root/.opam/4.10.0/lib/lwt/unix/liblwt_unix_stubs.a /root/.opam/4.10.0/lib/stublibs/dlllwt_unix_stubs.so)!'.a' 用于静态构建,而 '.so' 用于动态构建。

适合我的构建示例(Ubuntu Linux,ocaml 4.05.0):

ocamlfind ocamlopt -output-obj -dstartup -linkpkg -package lwt.unix -thread -o s.o event_emitter.ml
g++ -o event_emitter_program -I`ocamlfind c -where` -I $(ocamlopt -where) s.o interface.o main.cc -L `ocamlc -where` -L`ocamlfind query lwt.unix` -L`ocamlfind query threads` -lasmrun -llwt_unix_stubs -lunix -lbigarray -lcamlstr -lthreadsnat -lpthread -ldl

的使用$(ocamlopt -where)/libasmrun.a是个坏主意。使用 -lasmrun 和 -L 作为库路径的方式更为迂腐。

如果您对某些未定义的符号有疑问,最好使用readelf找出缺少哪个库。

于 2020-05-07T14:25:56.193 回答