How to compile OCaml and C/C++ code that depend on each other?

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

Here’s my interface.c file:

#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.

I can’t tell you anything about dune, but if you’re calling a function in a C++ program, that function should be defined with `extern “C” …’ -

extern “C” void *function_pointer_caller(…

If you look at the entry points in your compiled .o file, C++ will “mangle” their names to support linking overloaded functions. extern “C” disables that, and you’ll see the exact entry point as you named it.

yes, I’m doing that. Please take a look in my update in the question. main.cc is using external, and I’m not using dune for now.

Sorry, can’t see it. Where you define it, in interface.cc, it must be

extern "C" void function_pointer_caller(uint64_t pointer, char* message)
{
    void (*f)(char *);
    f = pointer;
}

The “C” is what fixes the name and makes it visible to non-C++ code.

thanks, it worked. Please see my update if you have time. I was also missing interface.o in g++. Anyways, I now have errors about Lwt_unix. Do you have any idea?

Can you recheck that you are using -output-obj switch as described in manual https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html#ss:c-embedded-code ?

you’re right, I changed to

ocamlfind ocamlopt -output-obj -o s.o -linkpkg -package lwt.unix -thread event_emitter.ml interface.c

g++ -o event_emitter_program -I $(ocamlopt -where) \
    s.o interface.o main.cc $(ocamlopt -where)/libasmrun.a -ldl

now I get too much undefined references, mostly to unix_ things: https://pastebin.com/0HMHzqdP

Changing to

g++ -o event_emitter_program -I $(ocamlopt -where) \
    s.o interface.o main.cc -L$(ocamlc -where) -lunix -lasmrun -ldl

eliminates the unix_ undefined references but I still get lwt_unix undefined references and some OCaml function missing references too: https://pastebin.com/ukyx9yE0

Can you try to link something of these?

/home/kakadu/.opam/4.10.0+fp+flambda/lib/lwt/unix/liblwt_unix_stubs.a
/home/kakadu/.opam/4.10.0+fp+flambda/lib/stublibs/dlllwt_unix_stubs.so
g++ -o event_emitter_program -I $(ocamlopt -where) \
    s.o interface.o main.cc -L$(ocamlc -where) -lunix -lasmrun -pthread /root/.opam/4.10.0/lib/lwt/unix/liblwt_unix_stubs.a -ldl

gives

https://pastebin.com/N9G3J7x5

@lucaszanella, The link is broken

I am not sure what you are trying to do: are you trying to make an ocaml program (one with its starting procedure in ocaml code) which links with c++ code in a static archive, or are you trying to make a c++ program which starts in a C++ main function but links in ocaml code? The first is much easier to accomplish, but then you need to build the program using ocamlopt and (because you need to have libstdc++ and other libraries also linked in order to use the c++ archive) have g++ as the linker. So leaving aside your ocaml libraries you end up linking up with something like:

ocamlopt -o event_emitter_program module1.cmx module2.cmx main.cmx \
         -cc g++ -ccopt -L[path-to-archive] -cclib -l[archive-name]

But of course you cannot leave your ocaml libraries out of account. So if you are not using dune with an appropriate libraries clause you will have to use findlib directly so it ends up looking something like:

ocamlfind ocamlopt -package lwt -package lwt.unix -linkpkg -thread -o  event_emitter_program ...

At this point it becomes a lot easier to use dune, and include the -cc, -ccopt and -cclib flags in a link_flags field in your executable stanza. You might also want to look at the foreign_archives field which is available in dune from around version 2.0 (I think), but you don’t have to use it.

As another poster has pointed out, the c++ functions that you call up directly from ocaml with the ‘external’ keyword must have C linkage in order to suppress name mangling. c++ functions that the interface functions call can of course have C++ linkage.

https://pastebin.com/N9G3J7x5

thanks but I’m actually trying to mak a C++ program that uses OCaml as a simple library, but this library also calls C++ code sometimes. That’s where the problem arises: one depends on the another, so I don’t know how to link one to the other

I think that you should upload current progress to gitlab or somewhere to let us see whole picture. I think that for better understanding we should use plain Makefiles to do compilation.

I have approximate Makefile that compiles program that does startup from C++, you can cut useless parts and prepare your demo.

The entire project I’m trying to compile is at https://github.com/lucaszanella/ocaml-c-interfaces/tree/master/src/cpp/c_receive_events

If you clone the repo and open with VSCode as in the Readme says, you can take advantage of the remote container extension and it’ll land on a dockerized environment exactly as mine. Anyways you don’t need it you can simply run it on your machine, the compiling instructions are in the build.sh.

I will create makefiles once I learn how to do it.

Summary of what I want to do: main.cc starts and calls OCaml with a pointer (an int64) which OCaml has to pass back, together with a buffer after, 5 seconds. Then the C interface invokes this pointer (which is a function) with the buffer.

Thank you for your help!

I did a small step (adding threads demo without lwt) but at least it links with lwt now. You probably should try to do next step yourself (by pedagogical purposes :slight_smile: ).

P.S. I created a PR

The problem is not that one depends on the other. The problem is that you want to start your program as a C++ program with the C++ main function as the program entry point. You can do that, and call up ocaml as a support library, but the tooling is less helpful when you do it that way. You do it by compiling your ocaml code with ocamlopt with the -output-obj option, and having ocamlopt link in all your .cmx/.cmxa dependencies. findlib can help you with that. However, when linking up your C++ code you then need to link in all the corresponding ocaml archive files (libunix.a, threadsnat.a and so forth, together with libasmrun.a, and the Lwt archives which you are using), for which as far as I am aware findlib won’t help you. It is a failure to include all the relevant archives when linking up with g++ which probably has caused the link-time errors you report. Doing it by hand in this way is both troublesome and brittle.

As far as I am aware dune is not going to help you with this. However, it will help you if you start your program as an ocaml executable and link in your C/C++ code as a support library.

If you are master of your own project it should make no difference from the design standpoint whether you start the program with an ocaml entry point or a C++ entry point. If you start it with an ocaml entry point you can immediately call up your C/C++ code via the ocaml FFI. If you are not master of your own project things may be different: but the tooling will help you much more if you start it as an ocaml program.

I wonder how often this comes up, how often people have to come up with workarounds/hacks. I had to do some workaround in order to allow ocaml to be called from Apache back in the day (this was Apache 1.x) when I wrote a “mod_ocaml” module for Apache. it was nearly 20yr ago, so I don’t remember much about it anymore, but it wasn’t fun, that I remember.

If it’s sufficiently common, maybe a good, high-quality sample project that demonstrates how to do it using various build-systems, would be useful? OTOH, if it’s pretty rare, no point in expending the effort.

Thanks, but I really really need to start from C++, because actually I want to port the MirageOS TCP/IP stack (written in OCaml) to a C++ code for my app that will run on Android, iOS, macOS, Windows and Linux, so there’s no way to begin with OCaml. I really need the TCP/IP stack as a library that I can use from C++ side.

I’m reading your answer carefully to try to understand if it’s possible or feasible to do that, because Mirage’s TCP/IP stack has too much dependencies so I don’t know if I’m going to be abe to build it as a shared library easily

yes, my idea is to make these examples available in my github as well as creating a page to help new people, because I could find no examples on google