Google

PLT mzc: MzScheme Compiler Manual


Foreign-Function Interface to C

The cffi.ss library of the compiler collection implements a subset of Gambit-C's foreign-function interface (see Marc Feeley's Gambit-C, version 3.0). The cffi.ss module defines two forms: c-lambda and c-declare. When interpreted directly or compiled to byte code, c-lambda produces a function that always raises exn:user, and c-declare raises exn:user. When compiled by mzc, the forms provide access to C. The mzc compiler implicitly imports cffi.ss into the top-level environment.

The c-lambda form creates a Scheme procedure whose body is implemented in C. Instead of declaring argument names, a c-lambda form declares argument types, as well as a return type. The implementation can be simply the name of a C function, as in the following definition of fmod:

(define fmod (c-lambda (double double) double "fmod")) 

Alternatively, the implementation can be C code to serve as the body of a function, where the arguments are bound to ___arg1 (three underscores), etc., and the result is installed into ___result (three underscores):

(define machine-string->float 
  (c-lambda (char-string) float 
     "___result = *(float *)___arg1;")) 

The c-lambda form provides only limited conversions between C and Scheme data. For example, the following function does not reliably produce a string of four characters:

(define broken-machine-float->string 
  (c-lambda (float) char-string 
     "char b[5]; *(float *)b = ___arg1; b[4] = 0; ___result = b;")) 

because the representation of a float can contain null bytes, which terminate the string. However, the full MzScheme API, which is described in Inside PLT MzScheme, can be used in a function body:

(define machine-float->string 
  (c-lambda (float) scheme-object 
     "char b[4]; *(float *)b = ___arg1; ___result = scheme_make_sized_string(b, 4, 1);")) 

The c-declare form declares arbitrary C code to appear after escheme.h or scheme.h is included, but before any other code in the compilation environment of the declaration. It is often used to declare C header file inclusions. For example, a proper definition of fmod needs the math.h header file:

(c-declare "#include <math.h>") 
(define fmod (c-lambda (double double) double "fmod")) 

The c-declare form can also be used to define helper C functions to be called through c-lambda.

The plt/collects/mzscheme/examples directory in the PLT distribution contains additional examples.

The c-lambda and c-declare forms are defined as follows:

  • (c-lambda (argument-type ···) result-type funcname-or-body-string) creates a Scheme procedure whose body is implemented in C. The procedure takes as many arguments as the supplied argument-types, and it returns one value. If return-type is void, the procedure's result is always void. The funcname-or-body-string is either the name of a C function (or macro) or the body of a C function.

    If funcname-or-body-string is a string containing only alphanumeric characters and _, then the created Scheme procedure passes all of its arguments to the named C function (or macro) and returns the function's result. Each argument to the Scheme procedure is converted according to the corresponding argument-type (as described below) to produce an argument to the C function. Unless return-type is void, the C function's result is converted according to return-type for the Scheme procedure's result.

    If funcname-or-body-string contains more than alphanumeric characters and _, then it must contain C code to implement the function body. The converted arguments for the function will be in variables ___arg1, ___arg2, ... (with three underscores in each name) in the context where the funcname-or-body-string is placed for compilation. Unless return-type is void, the funcname-or-body-string code should should assign a result to the variable ___result (three underscores), which will be declared but not initialized. The funcname-or-body-string code should not return explicitly; control should always reach the end of the body. If the funcname-or-body-string code defines the pre-processor macro ___AT_END (with three leading underscores), then the macro's value should be C code to execute after the value ___result is converted to a Scheme result, but before the result is returned, all in the same block; defining ___AT_END is primarily useful for deallocating a string in ___result that has been copied by conversion. The funcname-or-body-string code will start on a new line at the beginning of a block in its compilation context, and ___AT_END will be undefined after the code.

    Each argument-type must be one of the following:

    • bool
      Scheme range: any value
      C type: int
      Scheme to C conversion: #f => 0, anything else => 1
      C to Scheme conversion: 0 => #f, anything else => #t

    • char
      Scheme range: character
      C type: char
      Scheme to C conversion: character's ASCII value cast to signed byte
      C to Scheme conversion: ASCII value from unsigned cast mapped to character

    • unsigned-char
      Scheme range: character
      C type: unsigned char
      Scheme to C conversion: character's ASCII value
      C to Scheme conversion: ASCII value mapped to character

    • signed-char
      Scheme range: character
      C type: signed char
      Scheme to C conversion: character's ASCII value cast to signed byte
      C to Scheme conversion: ASCII value from unsigned cast mapped to character

    • int
      Scheme range: exact integer that fits into an int
      C type: int
      conversions: (obvious and precise)

    • unsigned-int
      Scheme range: exact integer that fits into an unsigned int
      C type: unsigned int
      conversions: (obvious and precise)

    • long
      Scheme range: exact integer that fits into a long
      C type: long
      conversions: (obvious and precise)

    • unsigned-long
      Scheme range: exact integer that fits into an unsigned long
      C type: unsigned long
      conversions: (obvious and precise)

    • short
      Scheme range: exact integer that fits into a short
      C type: short
      conversions: (obvious and precise)

    • unsigned-short
      Scheme range: exact integer that fits into an unsigned short
      C type: unsigned short
      conversions: (obvious and precise)

    • float
      Scheme range: real number
      C type: float
      Scheme to C conversion: number converted to inexact and cast to float
      C to Scheme conversion: cast to double and encapsulated as an inexact number

    • double
      Scheme range: real number
      C type: double
      Scheme to C conversion: number converted to inexact
      C to Scheme conversion: encapsulated as an inexact number

    • char-string
      Scheme range: string or #f
      C type: char*
      Scheme to C conversion: string => contained character array pointer, #f => NULL
      C to Scheme conversion: NULL => #f, anything else => new string created by copying the string

    • nonnull-char-string
      Scheme range: string
      C type: char*
      Scheme to C conversion: string's contained character array pointer
      C to Scheme conversion: new string created by copying the string

    • scheme-object
      Scheme range: any value
      C type: Scheme_Object*
      Scheme to C conversion: no conversion
      C to Scheme conversion: no conversion

    • (pointer string)
      Scheme range: an opaque c-pointer value identified as type string or #f
      C type: string*
      Scheme to C conversion: #f => NULL, c-pointer => contained pointer cast to string*
      C to Scheme conversion: NULL => #f, anything else => new c-pointer containing the pointer and identified as type string

    The return-type must be void or one of the arg-type keywords.

  • (c-declare code-string) declares arbitrary C code to appear after escheme.h or scheme.h is included, but before any other code in the compilation environment of the declaration. A c-declare form can appear only at the top-level or within a module's top-level sequence.

    The code-string code will appear on a new line in the file for C compilation. Multiple c-include declarations are concatenated (with newlines) in order to produces a sequence of declarations.