CallingFromCToSML - MLton Standard ML Compiler (SML Compiler)

MLton MLTONWIKIVERSION | CallingFromCToSML |
Home Index

MLton's ForeignFunctionInterface allows programs to export SML functions to be called from C. Suppose you would like export from SML a function of type `real * char -> int` as the C function `foo`. MLton extends the syntax of SML to allow expressions like the following:

    _export "foo": (real * char -> int) -> unit;

The above expression exports a C function named `foo`, with prototype

    Int32 foo (Real64 x0, Char x1);

The `_export` expression denotes a function of type `(real * char -> int) -> unit` that when called with a function `f`, arranges for the exported `foo` function to call `f` when `foo` is called. So, for example, the following exports and defines `foo`.

    val e = _export "foo": (real * char -> int) -> unit;
    val _ = e (fn (x, c) => 13 + Real.floor x + Char.ord c)

The general form of an `_export` expression is

    _export "C function name" attr... : cFuncTy -> unit;

The type and the semicolon are not optional. As with `_import`, a sequence of attributes may follow the function name.

MLton's `-export-header` option generates a C header file with prototypes for all of the functions exported from SML. Include this header file in your C files to type check calls to functions exported from SML. This header file includes `typedef`s for the types that can be passed between SML and C.

## Example

Suppose that `export.sml` is

    val e = _export "f": (int * real * char -> char) -> unit;
    val _ = e (fn (i, r, _) =>
               (print (concat ["i = ", Int.toString i,
                               "  r = ", Real.toString r, "\n"])
                ; #"g"))
    val g = _import "g" public: unit -> unit;
    val _ = g ()
    val _ = g ()

    val e = _export "f2": (Word8.word -> word array) -> unit;
    val _ = e (fn w =>
               Array.tabulate (10, fn _ => Word.fromLargeWord (Word8.toLargeWord w)))
    val g2 = _import "g2" public: unit -> word array;
    val a = g2 ()
    val _ = print (concat ["0wx", Word.toString (Array.sub (a, 0)), "\n"])

    val e = _export "f3": (unit -> unit) -> unit;
    val _ = e (fn () => print "hello\n");
    val g3 = _import "g3" public: unit -> unit;
    val _ = g3 ()

    (* This example demonstrates mutual recursion between C and SML. *)
    val e = _export "f4": (int -> unit) -> unit;
    val g4 = _import "g4" public: int -> unit;
    val _ = e (fn i => if i = 0 then () else g4 (i - 1))
    val _ = g4 13

    val (_, zzzSet) = _symbol "zzz" alloc: (unit -> int) * (int -> unit);
    val () = zzzSet 42
    val g5 = _import "g5" public: unit -> unit;
    val _ = g5 ()

    val _ = print "success\n"

Create the header file with `-export-header`.

    % mlton -default-ann 'allowFFI true'    \
            -export-header export.h         \
            -stop tc                        \
            export.sml

`export.h` now contains the following C prototypes.

    Int8 f (Int32 x0, Real64 x1, Int8 x2);
    Pointer f2 (Word8 x0);
    void f3 ();
    void f4 (Int32 x0);
    extern Int32 zzz;

Use `export.h` in a C program, `ffi-export.c`, as follows.

    #include <stdio.h>
    #include "export.h"

    /* Functions in C are by default PUBLIC symbols */
    void g () {
            Char8 c;

            fprintf (stderr, "g starting\n");
            c = f (13, 17.15, 'a');
            fprintf (stderr, "g done  char = %c\n", c);
    }

    Pointer g2 () {
            Pointer res;
            fprintf (stderr, "g2 starting\n");
            res = f2 (0xFF);
            fprintf (stderr, "g2 done\n");
            return res;
    }

    void g3 () {
            fprintf (stderr, "g3 starting\n");
            f3 ();
            fprintf (stderr, "g3 done\n");
    }

    void g4 (Int32 i) {
            fprintf (stderr, "g4 (%d)\n", i);
            f4 (i);
    }

    void g5 () {
            fprintf (stderr, "g5 ()\n");
            fprintf (stderr, "zzz = %i\n", zzz);
            fprintf (stderr, "g5 done\n");
    }

Compile `ffi-export.c` and `export.sml`.

    % gcc -c ffi-export.c
    % mlton -default-ann 'allowFFI true' \
             export.sml ffi-export.o

Finally, run `export`.

    % ./export
    g starting
    ...
    g4 (0)
    success

## Download

- export.sml

- ffi-export.c

Last edited on 2006-11-02 17:34:21 by MatthewFluet.