Introduction - FFI 0.2
Description: FFI 0.2

Next: C Declarations, Previous: Top, Up: Top

## 1 Introduction

This FFI provides Scheme syntax for calling C functions and accessing C data. The functions and data structures are declared in a case sensitive `.cdecl` file, which is used by a shim generator to produce callout and callback trampoline functions. The trampolines are compiled and linked to the C toolkit, producing a shared object that Scheme can dynamically load.

### Synopsis

Examples of the new syntax:

         (C-include "prhello")

         (malloc (C-sizeof "GdkEvent"))
         => #[alien 42 0x081afc60]

         (C->= #@42 "GdkEvent any type" 14)

         (C-> #@42 "GdkEvent any type")
         => 14

         (C-enum "GdkEventType" 14)
         => |GDK_MAP|

         (C-enum "GDK_MAP")
         => 14

         (C-sizeof "GdkColor")
         => 12

         (C-offset "GdkColor blue")
         => 8

         (C-array-loc #@43 "GdkColor" (C-enum "GTK_STATE_NORMAL"))
         => #[alien 44 0x081afc60] ; New alien.

         (C-array-loc! #@43 "GdkColor" (C-enum "GTK_STATE_PRELIGHT"))
         => #[alien 43 0x081afc78] ; Modified alien.

         (C-call "gtk_window_new" retval args ...)
         => #!unspecific

         (C-callback "delete_event")
         => #[alien-function 44 Scm_delete_event]

         (C-callback (lambda (window event) ...))
         => 13                     ; A fixnum registration ID.

### Summary

A Scheme-like declaration of a toolkit's C functions, constants, and data types is given in a case sensitive `.cdecl` file. The C declarations look like this:

         (extern (* GtkWidget)
                 gtk_window_new
                 (type GtkWindowType))

         (typedef GtkWindowType
                  (enum
                   (GTK_WINDOW_TOPLEVEL)
                   (GTK_WINDOW_POPUP)))

The `c-generate` procedure reads these declarations and writes three files: `library-types.bin` (a fasdump of the parsed declarations), `library-const.c` (a C program that prints C constants and struct offsets), and `library-shim.c` (trampoline functions adapting Scheme procedure application to C function call). The `-const.c` program generates a `-const.scm` file, which can be syntaxed to produce a `-const.bin` file.

         (load-option 'FFI)
         (c-generate "prhello" "#include <gtk/gtk.h>")

The `-types.bin` and `-const.bin` files together provide the information needed to expand `C-...` syntax, and are only needed at syntax time. The compiled `-shim.so` file is used at run time, dynamically loaded into the Scheme machine. Compiling and Linking, which describes these files in more detail, and shows how they might be built and installed.

         (C-include "prhello")

The `C-include` syntax loads the `-types.bin` and `-const.bin` files at syntax time. It should appear at the top level of any file containing `C-...` syntax, or be evaluated in the syntax environment of such code.

The `C-call` syntax arranges to invoke a callout trampoline. Arguments to the trampoline can be integers, floats, strings or aliens (non-heap pointers, to C data structures, see Alien Data).

         (let ((alien (make-alien '|GtkWidget|)))
           (C-call "gtk_window_new" alien type)
           (if (alien-null? alien) (error "could not open new window"))
           alien)

The `C-callback` syntax is used when registering a Scheme callback trampoline. The two forms of the syntax provide two arguments for the registration function: the callback trampoline's address, and a “user data” argument. When the toolkit calls the trampoline, it must provide the fixnum-sized user data as an argument.

         (C-call "g_signal_connect" window "delete_event"
                 (C-callback "delete_event")     ; e.g. &Scm_delete_event
                 (C-callback                     ; e.g. 314
                   (lambda (window event)
                     (C-call "gtk_widget_destroy" window)
                     0)))

The first use of `C-callback` (above) expands into a callback trampoline address — an alien function. The second use evaluates to a fixnum, which is associated with the given Scheme procedure.

The `C->` and `C->=` syntaxes peek and poke values into alien data structures. They take an alien and a constant string specifying the alien data type and the member to be accessed (if any).

         (C-> alien "GdkRectangle y")
         ==>
         (#[primitive c-peek-int] alien 4)

         (C->= alien "GdkRectangle width" 0)
         ==>
         (#[primitive c-poke-int] alien 8 0)

         (C-> alien "GdkEvent any type")
         ==>
         (#[primitive c-peek-int] alien 0)

         (C-> alien "gfloat")
         ==>
         (#[primitive c-peek-float] alien 0)

A three argument form of the syntax provides an alien to receive a peeked pointer. This avoids consing a new alien.

         (C-> alien "GtkWidget style" alien)

The above syntax is understood to say “The data at this `alien` address is a GtkWidget. Load its `style` member (an alien address), into `alien` (clobbering `alien`'s old address).”

The `C-enum`, `C-sizeof` and `C-offset` syntaxes all transform into integer constants. The last two transform into a padded byte size and a byte offset respectively.

         (C-enum "GTK_WINDOW_POPUP")
         ==>
         1

         (C-sizeof "GdkColor")
         ==>
         12

         (C-offset "GdkColor blue")
         ==>
         8

The two element form of the `C-enum` syntax can be used to find the name of a constant given its runtime value. It expects the name of an enum type in a constant string. If the runtime (second) argument is not one of the constants declared by that type, the returned value is `#f`.

         (C-enum "GdkEventType" (C-> #@42 "GdkEvent any type"))
         => |GDK_MAP|

The `c-array-loc` and `c-array-loc!` syntaxes compute the locations of C array elements. They can be used to advance a scan pointer or locate an element by its index. The examples in the synopsis might expand as shown here.

         (C-array-loc #@43 "GdkColor" (C-enum "GTK_STATE_NORMAL"))
         ==>
         (alien-byte-increment #@43 (* (C-sizeof "GdkColor")
                                       (C-enum "GTK_STATE_NORMAL")))
         ==>
         (alien-byte-increment #@43 (* 12 0))
         => #@44

         (C-array-loc! #@43 "GdkColor" (C-enum "GTK_STATE_PRELIGHT"))
         ==>
         (alien-byte-increment! #@43 (* (C-sizeof "GdkColor")
                                        (C-enum "GTK_STATE_PRELIGHT")))
         ==>
         (alien-byte-increment! #@43 (* 12 2))
         => #@43

A simple scan of characters in the wide string `alien` might look like this.

         (let ((len (C-> alien "toolkit_string_type int_member"))
               (scan (C-> alien "toolkit_string_type array_member")))
           (let loop ((n 0))
             (if (< n len)
                 (let ((wchar (C-> scan "wchar")))
                   (process wchar)
                   (C-array-loc! scan "wchar" 1)
                   (loop (1+ n))))))

That is a quick look at the facilities. The next section describes the C declaration language, and the following sections examine the FFI's syntax and runtime facilities in detail. Final sections provide an example program and show how its dynamically loaded shim is built.