# Dune project with a C/C++ ctypes foreign defined function

**URL:** <https://discuss.ocaml.org/t/dune-project-with-a-c-c-ctypes-foreign-defined-function/12696>\
**Category:** Learning\
**Tags:** ctypes\
**Created:** [July 26, 2023, 12:36pm UTC](https://discuss.ocaml.org/t/dune-project-with-a-c-c-ctypes-foreign-defined-function/12696 "2023-07-26T12:36:52Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![chamaeleon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chamaeleon/32/4605_2.png) [@chamaeleon](https://discuss.ocaml.org/u/chamaeleon)\
**Post date:** [July 26, 2023, 12:36pm UTC](https://discuss.ocaml.org/t/dune-project-with-a-c-c-ctypes-foreign-defined-function/12696/1 "2023-07-26T12:36:52Z")

</div>

Hi all, after a rather long hiatus from Ocaml I’m looking at trying to make use of the language once again for fun little projects in my spare time. The other day I was thinking about trying to wrap some C++ code (but I’m using C for this test) in an Ocaml library, and I ran into a something that confuses me a bit.

In a nutshell, I can create a ctypes foreign function in the bin executable code, but creating a ctypes foreign function in the lib directory and naively adding the library to the bin executable fails.

Steps I take (which may miss something very essential…)

1. dune init project cpptest
2. Create lib/fib.ml, lib/fib.c, and lib/fib.h

lib/fib.ml content:

```auto
open Ctypes
open Foreign
let fib = foreign "fib" (int @-> returning int)

```

lib/fib.c content:

```auto
#include "fib.h"

int fib(int n)
{
    if (n < 2) return 1;
    else return fib(n - 1) + fib(n - 2);
}

```

lib/fib.h content:

```auto
#ifndef CPPTEST_FIB_H
#define CPPTEST_FIB_H

#ifdef __cplusplus
extern "C" {
#endif

int fib(int n);

#ifdef __cplusplus
}
#endif

#endif /* CPPTEST_FIB_H */

```

1. Add libraries and foreign stubs to lib/dune

lib/dune

```auto
(library
 (name cpptest)
 (foreign_stubs (language c) (names fib))
 (libraries ctypes ctypes-foreign))

```

1. Update bin/main.ml and bin/dune

bin/main.ml

```auto
open Printf
open Cpptest

let () = printf "%d\n" (Fib.fib 20)

```

bin/dune

```auto
(executable
 (public_name cpptest)
 (name main)
 (libraries cpptest))

```

1. dune build is successful
2. dune exec cpptest results in in a symbol lookup error

```auto
Fatal error: exception Dl.DL_error("_build/install/default/bin/cpptest: undefined symbol: fib")

```

1. On the other hand, putting fib.c and fib.h in bin/, and add the foreign declaration of fib in main.ml works.

bin/main.ml

```auto
open Printf
open Ctypes
open Foreign
let fib = foreign "fib" (int @-> returning int)
let () = printf "%d\n" (fib 20)

```

bin/dune

```auto
(executable
 (public_name cpptest)
 (name main)
 (foreign_stubs (language c) (names fib))
 (libraries ctypes ctypes-foreign))

```

1. dune build is successful (just like when trying the library version)
2. dune exec cpptest calls the C fib() function successfully (in contrast to the library version)

```auto
10946

```

Any insights into what I am overlooking would be very welcome!

---

<div class="post-metadata">

**Author:** ![yallop](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yallop/32/517_2.png) [@yallop](https://discuss.ocaml.org/u/yallop)\
**Post date:** [July 26, 2023, 9:35pm UTC](https://discuss.ocaml.org/t/dune-project-with-a-c-c-ctypes-foreign-defined-function/12696/2 "2023-07-26T21:35:33Z")

</div>

ctypes-foreign looks up the symbol dynamically, and by default it looks in the current executable.

You can make it search in another location instead by passing the optional [`from`](https://github.com/yallop/ocaml-ctypes/blob/0.21.1/src/ctypes-foreign/foreign.mli#L12) argument to `foreign`, e.g.:

```ocaml
let lib = Dl.dlopen ~filename:"_build/default/lib/dllcpptest_stubs.so"
            ~flags:Dl.[RTLD_NOW; RTLD_GLOBAL]

let fib = foreign "fib" (int @-> returning int)
            ~from:lib

```

In the long term it might be easier to switch to the stub-generation interface so that symbols are resolved statically.

---

<div class="post-metadata">

**Author:** ![chamaeleon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chamaeleon/32/4605_2.png) [@chamaeleon](https://discuss.ocaml.org/u/chamaeleon)\
**Post date:** [July 27, 2023, 11:33pm UTC](https://discuss.ocaml.org/t/dune-project-with-a-c-c-ctypes-foreign-defined-function/12696/3 "2023-07-27T23:33:37Z")

</div>

Thank you for the reply!

I’m not sure if I _can_ resolve symbols statically, if I’m creating a library for use by some arbitrary other program, unless this stub-generation you speak of allows for it for the library (are you talking about Dune’s ctypes support which mention stub generation, or are you referring to something else)?

This exercise is for me to test the waters with respect to wrapping some C++ code in a library form. Because it is C++, I need to create a suitable set of C functions as a translation layer, and the C files in question seemingly always end up as a shared object for my library (and would have to be, I think, in order to support Ocaml bytecode programs if nothing else, although this is not a set-in-stone constraint for me).

As a side-note, if I set LD\_LIBRARY\_PATH to a directory containing dllcpptest\_stubs.so (like the build lib directory), it is possible to have the wrapper ml file refer to the shared object by filename only, and not a complete path, but it feels like maybe I’m sweeping a problem under the rug if I do.
