Как скомпилировать код OCaml и C / C ++, которые зависят друг от друга - PullRequest
1 голос
/ 05 мая 2020

У меня проблемы с определением сигнатуры функции C void, которая принимает uint64_t и char*. Я пробовал int64 -> string -> _.

Я также не знаю, как скомпилировать мой файл C ++ (с интерфейсом C) вместе

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

интерфейс. 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);
}

основной. 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. Может быть, можно скомпилировать .ml в объект .so и связать его с. cc с интерфейсом C?

В любом случае, я изменил 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

Обратите внимание, что я подключаю interface.o из предыдущей команды (ocamlopt) и ссылку в g ++, потому что ocamlopt фактически передает C файлы компилятору C.

Я не знаю, почему он жалуется на Lwt_unix вещи, поскольку я уже скомпилировал с их в ocamlopt.

Ответы [ 2 ]

2 голосов
/ 11 мая 2020

Утилита 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) использование этой опции для более чем одного модуля компиляции может легко привести к конфликтам символов.

1 голос
/ 07 мая 2020

Вам следует быть более осторожными с «предупреждающими» сообщениями и не полагаться на вещи, которые привязывают процесс сборки к вашей конфигурации. Пожалуйста, избегайте использования консоли root!

[WARNING] Running as root is not recommended

В среде root путь к библиотекам может быть сложным.

Согласно вашему 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' предназначен для сборки stati c, а '.so' - для динамической c.

Пример сборки, которая работает для меня (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 - плохая идея. Это более педантичный c способ использовать -lasmrun и -L для пути к библиотекам.

Если у вас есть проблемы с некоторыми неопределенными символами, лучше всего использовать readelf , чтобы узнать, какая библиотека отсутствует.

...