Reputation: 1850
I'm having problems defining the signature of a C void function that accepts an uint64_t
and a char*
. I tried int64 -> string -> _
.
I also don't know how to compile my C++ file (with C interface) together
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;
}
I think there's no way to compile a .cc together with a .ml, because a .cc does not necessairly have a C interface. Maybe it's possible to compile the .ml to a .so object and link it to the .cc with the C interface?
Anyways, I did change interface.cc
to interface.c
and added interface.c
to the ocamlopt
command:
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
The first command compiles ok, but g++ gives
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
Notice that I plug the interface.o
from the previous command (ocamlopt
) and link in g++, because ocamlopt
actually passes C files to a C compiler.
I don't know why it complains about Lwt_unix
things, since I already compiled with them in ocamlopt
.
Upvotes: 2
Views: 593
Reputation: 709
You should be more careful with “warning” messages and not rely on things that bind the building process to your configuration. Avoid usage of root console please!
[WARNING] Running as root is not recommended
In the root environment path to libraries can be complicated.
According to your pastebin I see that linker is unable to locate the lwt_unix_stubs library. And don't mix '.a' and '.so' libraries please (/root/.opam/4.10.0/lib/lwt/unix/liblwt_unix_stubs.a /root/.opam/4.10.0/lib/stublibs/dlllwt_unix_stubs.so
)! '.a' is for static build while '.so' is for dynamic.
Example of build that works for me (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
Usage of $(ocamlopt -where)/libasmrun.a
is bad idea. It's more pedantic way to use -lasmrun and -L for path to libraries.
If you have problems with some undefined symbols, it's best to use readelf to find out which library is missing.
Upvotes: 1
Reputation: 35210
The ocamlopt
utility is doing a bit more than just linking the specified compilation units and libraries, it also embeds the architecture startup code, which is not included in the asmrun
library. It is possible to obtain this code using the -output-obj
, but I find it a little bit error-prone and hard to maintain as it is not really composable1.
So, it is better to rely on ocamlopt to build the final binary, which will consist of modules written in different languages. Each unit will be built with the appropriate to the language, in which it is written, tool. Let's build the events.ml
compilation unit:
ocamlfind ocamlopt -c -package lwt.unix events.ml
Now, let's build the interface (we can use cc
here, but using ocamlopt will save us a bit of hassle of providing the location of the includes)
ocamlfind ocamlopt -c interface.c
Now, let's build the C++ part (but first fix it and use caml_main
instead of caml_startup
since we want the native runtime).
g++ -c main.cc -o main.o -I `ocamlc -where`
Now, when we have all units we're ready for the final linking command:
ocamlfind ocamlopt events.cmx interface.o main.o -package lwt.unix -package lwt -thread -linkpkg -o emitter
Et voila, we can run our program with ./emitter
.
1) using this option for more than one compilation unit may easily lead to symbol conflicts.
Upvotes: 3