-
Notifications
You must be signed in to change notification settings - Fork 95
- How can I install ctypes?
- How should I install libffi on Mac OS X?
- How can I build ctypes with debug support?
- Does ctypes work on Windows?
- How can I print ctypes values in the toplevel?
- What's the build process for code that uses stub generation ?
- 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?
- What's the best way to bind C functions that act on strings?
- How can I deal with null pointers?
- Does ctypes support bigarrays?
- 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?
- How can I declare an incomplete type?
- How can I define a type that refers to itself?
- 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"?
- 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?
- 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?
- How can I expose OCaml code to C?
- How can I improve the performance of my ctypes code?
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.
You'll need pkg-config.
You'll need redhat-rpm-config.
The default version of libffi on Mac OS X 10.8 is too old. Use Homebrew to install a later version.
Build one of the release packages or the trunk from source using the following command:
make DEBUG=true
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.
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 }
There are examples in the async_ssl (using jbuilder) and lz4 (using ocamlbuild) packages.
- There is a tutorial on the wiki
- Real World OCaml Chapter 19 has a more extensive tutorial.
- The API documentation describes each ctypes type and function.
- If you want to understand a particular feature the unit tests are often a useful starting point.
- The examples in the distribution show how to write larger-scale bindings to C code.
- A number of real-world examples of ctypes bindings are available: see Who is using 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
The ctypes OPAM page lists a number of packages that use ctypes.
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.
- The easiest approach is to use the
string
type descriptor, which converts between OCaml strings andchar *
:
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.
- 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.
- 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.
- 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.
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.
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?
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?.
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.