Next: , Previous: , Up: FFI  


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 expanded into 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. See 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 expanded 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 bytevectors) or aliens (non-heap pointers, to C data structures, see Alien Data). If a string argument might contain non-ASCII characters (code points U+0080 and larger), it should be converted to a bytevector e.g. by string->utf8, else an error could be signaled.

(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 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.


Next: C Declarations, Previous: FFI, Up: FFI