Strange error when calling external c functions

I’m trying to call Python’s C APIs in OCaml efficiently.

external pyinit : unit -> unit = "Py_Initialize"
external errprint : unit -> unit = "PyErr_Print"
external compile : string -> string -> int -> pyo = "Py_CompileString"

let main() =
  let _ = pyinit() in
  let _ = compile "print(1)" "a.py" 257 in
  errprint()

sh> dynjit dune exec myproj
  File "a.py", line 1
    print(1)
    ^
SyntaxError: invalid syntax

This is really a strange error, everything is fine but it doesn’t work.

I’ve tried invoking the C function Py_CompileString in Python with python’s ctypes, it works.
Also I’ve tried this in another programming language with its dynamic link interfaces, things also work.

However the equivalent code in OCaml causes this very strange error. The c function arguments must be handled correctly otherwise I cannot see “print(1)” or “a.py” in the error report.

If I incorrectly put the whitespaces in the head of the code, err_print can also report what the syntax error is.

So strange… Any suggestions?

You are lucky your code doesn’t segfault : )

When OCaml calls a C function, it generally needs to go through a wrapper function in order to handle the interaction with OCaml’s GC and the conversion between C and OCaml data types.

In particular, external is expected to be used only for functions that have specific signatures and follow specific conventions, and it is not meant as a general way to call arbitrary C functions.

You can learn more about the OCaml/C FFI by heading over to https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html.

Luckily for you, OCaml has a version of ctypes which works very well. You should be able to use to call the Python API functions. Head over https://github.com/ocamllabs/ocaml-ctypes to learn more about it.

Cheers,
Nicolás

1 Like

Thanks a lot for posting these references and giving me suggestions.

I don’t want to use ctypes because my application is performance sensitive, actually it’s for generally speed up python programs, so ocaml-ctypes are quite slow here. Although there is a cstubs approach by ocaml-ctypes which is much faster, I tried pyml(they said it’s implemented via cstubs) but still the performance didn’t satisfy me.
I know how to write wrappers and make custom primitives, but I don’t want to write lots of c code.
Also there’re many C APIs, if I can directly use external functions, I’d feel satisfied…

I now successfully workaround things, by making only one c function to right shift interger 1 bit(nativeint and int32 requires manual wrapper)… and I can keep my types used in external functions always a subset of {int64, int, string, unit, i32} where i32 is just an opaque type for representing c int.

Is this okay to directly call those external functions? It seems things work fine now.

Besides, I feel so eager that I want to know all details about external keyword…
It seems the documentation just lists a few extensions ([@@unboxed|, etc.), not very clean as well. And their corresponding syntax before 4.03.0(I’m using modular-implicits) is also hard to find in the documentations available online.

Cheers,
Taine