Skip to content
ygrek edited this page Aug 30, 2021 · 18 revisions

Building and installing ctypes

Using ctypes

Support and documentation

Questions about C types

Pointers, strings and arrays

Calling functions

Function pointers

Structs and unions

Enums

Memory management

Miscellaneous questions


Building and installing ctypes

How can I install ctypes?

The easiest way to install ctypes is to use the OPAM package manager. You should first ensure that libffi is installed using your operating system's package manager. You'll also need some distribution-specific packages, see below. Then type

opam install ctypes

If you'd prefer not to use OPAM then you can obtain the source for ctypes from the releases page. In order to build the source you'll need libffi, findlib and a basic Unix-like environment with GNU Make. In order to run the tests you'll also need OUnit.

Debian and derivatives - Ubuntu, Mint, etc.

You'll need pkg-config.

Fedora

You'll need redhat-rpm-config.

How should I install libffi on Mac OS X?

The default version of libffi on Mac OS X 10.8 is too old. Use Homebrew to install a later version.

How can I build ctypes with debug support?

Build one of the release packages or the trunk from source using the following command:

make DEBUG=true

Does ctypes work on Windows?

Ctypes is written in OCaml and standard C, and is intended to work on all common platforms, including Windows. If you run into problems using ctypes on Windows, please give details on the issue tracker.

Using ctypes

How can I print ctypes values in the toplevel?

Use the ctypes.top package. Loading the package installs printers for various ctypes values. You can load the package using Findlib's #require directive:

# #use "topfind";;
- : unit = ()
Findlib has been successfully loaded. Additional directives:
  #require "package";;      to load a package
  #list;;                   to list the available packages
  #camlp4o;;                to load camlp4 (standard syntax)
  #camlp4r;;                to load camlp4 (revised syntax)
  #predicates "p,q,...";;   to set these predicates
  Topfind.reset();;         to force that packages will be reloaded
  #thread;;                 to enable threads

- : unit = ()
# #require "ctypes.top";;
$HOME/.opam/4.01.0/lib/ocaml/unix.cma: loaded
$HOME/.opam/4.01.0/lib/ocaml/bigarray.cma: loaded
$HOME/.opam/4.01.0/lib/ocaml/str.cma: loaded
$HOME/.opam/4.01.0/lib/ctypes: added to search path
$HOME/.opam/4.01.0/lib/ctypes/ctypes.cma: loaded
$HOME/.opam/4.01.0/lib/ctypes/ctypes-top.cma: loaded
# open Ctypes;;

Once the package is loaded, the results of expressions that evaluate to C type descriptions are printed using C declaration syntax. For example, the type "array of 10 floats" is printed as float[10]:

# array 10 float;;
- : float carray typ = float[10]

and the type "function accepting two ints and returning a pointer to an array of 10 chars" is printed as follows:

# int @-> int @-> returning (ptr (array 10 char));;
- : (int -> int -> char carray ptr) fn = char(*(int, int))[10]

C values are also printed out, in a form similar to C's initializer syntax. Here's how ctypes prints an array of three floats:

# CArray.of_list float [1.0; 2.0; 3.0];;
- : float carray = { 1, 2, 3 }

What's the build process for code that uses stub generation ?

There are examples in the async_ssl (using dune) and lz4 (using ocamlbuild) packages.

Why does stub generation use functors?

Does stub generation support the "noalloc" and "float" flags?

What's the purpose of the -Wl,-no-as-needed flag?

Support and documentation

What ctypes documentation is available?

Where can I get support for ctypes?

There's a mailing list hosted on lists.ocaml.org and mirrored on gmane:

If you find a bug, or would like to propose or contribute a new feature, please visit the GitHub issue tracker

Who is using ctypes?

The ctypes OPAM page lists a number of packages that use ctypes.

Questions about C types

Pointers, strings and arrays

What's the best way to bind C functions that act on strings?

There are a number of options available; which one is best depends on your situation. Let's consider the standard C puts function as an example.

  1. The easiest approach is to use the string type descriptor, which converts between OCaml strings and char *:
let puts = foreign "puts"
   (string @-> returning int)

In order to avoid problems with the garbage collector, string copies OCaml strings into immovable storage when passing them to C. The copying means that it's unlikely that you'll run into unexpected interactions with the GC, but makes string unsuitable for binding to C functions that write into the string, or for cases where performance is critical.

When the memory is owned by C code -- for example, when a C function passes a struct to OCaml with fields already initialized -- then using the string view works well: reading a string field creates an OCaml copy, which persists as long as needed.

However, when things are the other way round -- i.e. when you're creating or initializing the struct in OCaml before passing it to C -- then the string view isn't a good choice, because there's no way to manage the lifetime of the C copy of the OCaml string that's written to the struct field.

  1. A second alternative is to use Bigarrays:
let puts_ = foreign "puts"
   (ptr char @-> returning int)
let puts arr = puts_ (bigarray_start array1 arr)

Bigarray values are stored outside the OCaml heap, so there's no possibility of the garbage collector unexpectedly moving the memory around. There isn't any support in the standard library for converting between strings and bigarrays. However, Core's bigstring module has conversion functions that may make interoperability easier.

  1. A third alternative is to use CArray:
let puts_ = foreign "puts"
   (ptr char @-> returning int)
let puts arr = puts_ (CArray.start arr)

As with bigarrays, CArray values are stored outside the OCaml heap. For code that deals strings, or other arrays of primitive types, CArray offers few advantages over bigarrays; its real benefits are seen in more complex situations involving arrays of structs, pointers, or other non-arithmetic types.

  1. The final alternative is to use ocaml_string:
let puts = foreign "puts"
   (ocaml_string @-> returning int)

The ocaml_string descriptor acts very much like string, except that no copying takes place; instead, ocaml_string passes a pointer into the OCaml heap directly to C. This behaviour is more appropriate for binding C functions that write into the string, but there are a number of caveats. If the garbage collector runs between the time that you pass the pointer to C and the time that C accesses the memory then the accesses may be invalid. More concretely, you must be confident that the C code doesn't capture the passed pointer to be accessed later and that no OCaml code can run during the C call. More concretely, ocaml_string is unsuitable for binding to functions that call back into OCaml, or that release the runtime lock. The introduction of multicore is likely to introduce further subtleties.

How can I deal with null pointers?

Does ctypes support bigarrays?

Calling functions

How can I call variadic functions?

How can I call C macros?

How can I call compiler builtins?

How can I bind to functions that set errno?

Function pointers

How can I pass OCaml functions to C?

The funptr constructor describes a value that appears as a function in OCaml and a function pointer in C. For example, consider the qsort sorting function from the C standard library. The fourth argument to qsort is a function pointer denoting the comparison function to use in the sort:

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

You can describe qsort with ctypes as follows:

let qsort = foreign "qsort"
      (ptr void @-> size_t @-> size_t @->
       funptr (ptr void @-> ptr void @-> returning int) @->
       returning void)

As the inferred type indicates, the fourth argument on the OCaml side is a regular OCaml function:

val qsort : unit ptr -> Unsigned.size_t -> Unsigned.size_t ->
           (unit ptr -> unit ptr -> int) ->
           unit

The tutorials on the wiki and in Real World OCaml give further details. There are also complete examples in the test-cstdlib and test-higher_order directories and in the FTS binding (see fts_compar) in the distribution.

What is the lifetime of an OCaml function passed to C?

Structs and unions

How can I declare an incomplete type?

Incomplete types are the closest thing C has to abstract types. The following declaration declares an incomplete type struct foo:

struct foo;

If this is the only declaration in scope for struct foo then the C compiler will reject code that attempts to make use of the representation. For example, without a full declaration in scope there's no way to access members, depend on the size (whether directly as with sizeof, or indirectly as with pointer arithmetic), or allocate objects of type struct foo "on the stack".

The ctypes equivalent of the incomplete declaration above is a call to structure without a corresponding call to seal:

let foo = structure "foo"

As in C, you won't be able to access members or compute the size of an incomplete type such as foo.

See also How can I define a type that refers to itself?

How can I define a type that refers to itself?

This works much the same as in C. Here's a typical C definition of a type which refers to itself:

struct node {
  struct node *next;
  int payload;
};

Breaking this down a little, the first line declares an incomplete type struct node, which is in scope for the rest of the definition (and subsequently). The closing right brace } completes the type. Here's an equivalent ctypes definition:

let node : [`node] structure typ = structure "node"
let next = field node "next" (ptr node)
let payload = field node "payload" int
let () = seal node

The first line calls structure to create an incomplete type and binds it to node, which is in scope for the rest of the definition (and subsequently). The closing call to seal completes the type.

See also How can I declare an incomplete type?.

How can I represent structures with bit-field members?

How does ctypes handle struct layout?

How can I represent packed structs?

How can represent structs that use the "struct hack"?

Enums

How can I represent enums?

Memory management

How can I attach finalisers to objects created by ctypes?

When should I use finalisers?

What do I need to know about the garbage collector?

When does ctypes copy objects?

Miscellaneous questions

What's the purpose of views?

How can I cast between types?

How can I access global C variables?

How can I bind to C++ code?

The easiest approach is to provide a C-compatible interface by using extern "C" declarations in your C++ code. If you're not able to modify the C++ code you may be able to perform name mangling by hand. There is an issue open proposing adding support for binding C++ libraries to ctypes.

How can I expose OCaml code to C?

How can I improve the performance of my ctypes code?

Clone this wiki locally