282 lines
12 KiB
Racket
282 lines
12 KiB
Racket
#lang scribble/doc
|
|
@(require scribble/manual
|
|
scribble/struct
|
|
(for-label scheme/base
|
|
compiler/cffi
|
|
mzlib/include))
|
|
|
|
@(define mzc (exec "mzc"))
|
|
@(define cpp tt)
|
|
@(define inside @other-manual['(lib "scribblings/inside/inside.scrbl")])
|
|
|
|
@(define (lines . l)
|
|
(make-table #f
|
|
(map (lambda (l)
|
|
(list
|
|
(make-flow
|
|
(list
|
|
(if (table? l)
|
|
l
|
|
(make-paragraph (list l)))))))
|
|
l)))
|
|
|
|
@(define (argtype scm desc c . cases)
|
|
(item (apply lines
|
|
scm
|
|
@elem{Scheme range: @|desc|}
|
|
@elem{C type: @|c|}
|
|
cases)))
|
|
|
|
@(define (toC . content)
|
|
(apply elem "Scheme to C conversion: " content))
|
|
@(define (toScheme . content)
|
|
(apply elem "C to Scheme conversion: " content))
|
|
@(define tosObvious
|
|
(elem "conversions: (obvious and precise)"))
|
|
@(define goesto 'rarr)
|
|
|
|
|
|
|
|
@title{@scheme[c-lambda]: C FFI via @exec{mzc}}
|
|
|
|
@defmodule[compiler/cffi]{
|
|
|
|
The @schememodname[compiler/cffi] module relies on a C compiler to
|
|
statically construct an interface to C code through directives
|
|
embedded in a Scheme program. The library implements a subset of
|
|
@as-index{Gambit-C}'s foreign-function interface @cite["Feeley98"].}
|
|
|
|
The @schememodname[scheme/foreign] library is a better interface for
|
|
most tasks; see @other-manual['(lib
|
|
"scribblings/foreign/foreign.scrbl")] for more information on
|
|
@schememodname[scheme/foreign]. See also @|inside|, which describes
|
|
PLT Scheme's C-level API for extending the run-time system.
|
|
|
|
The @schememodname[compiler/cffi] library defines three forms:
|
|
@scheme[c-lambda], @scheme[c-declare], and @scheme[c-include]. When
|
|
interpreted directly or compiled to byte code, @scheme[c-lambda]
|
|
produces a function that always raises @scheme[exn:fail], and
|
|
@scheme[c-declare] and @scheme[c-include] raise
|
|
@scheme[exn:fail]. When compiled by @exec{mzc --extension}, the forms
|
|
provide access to C. Thus, @schememodname[compiler/cffi] is normally
|
|
required by a module to be compiled via @|mzc|. In addition, the
|
|
@|mzc| compiler implicitly imports @schememodname[compiler/cffi] into
|
|
the top-level environment for non-@scheme[module] compilation.
|
|
|
|
The @scheme[c-lambda] form creates a Scheme procedure whose body is
|
|
implemented in C. Instead of declaring argument names, a
|
|
@scheme[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 @scheme[fmod]:
|
|
|
|
@schemeblock[
|
|
(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 @cpp{___arg1} (three
|
|
underscores), etc., and the result is installed into @cpp{___result}
|
|
(three underscores):
|
|
|
|
@schemeblock[
|
|
(define machine-string->float
|
|
(c-lambda (char-string) float
|
|
"___result = *(float *)___arg1;"))
|
|
]
|
|
|
|
The @scheme[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:
|
|
|
|
@schemeblock[
|
|
(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 @cpp{float} can contain null bytes,
|
|
which terminate the string. However, the full MzScheme API, which is
|
|
described in @|inside|, can be used in a function body:
|
|
|
|
@schemeblock[
|
|
(define machine-float->string
|
|
(c-lambda (float) scheme-object
|
|
"char b[4];"
|
|
"*(float *)b = ___arg1;"
|
|
"___result = scheme_make_sized_byte_string(b, 4, 1);"))
|
|
]
|
|
|
|
The @scheme[c-declare] form declares arbitrary C code to appear after
|
|
@filepath{escheme.h} or @filepath{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 @scheme[fmod] needs the @filepath{math.h} header
|
|
file:
|
|
|
|
@schemeblock[
|
|
(c-declare "#include <math.h>")
|
|
(define fmod (c-lambda (double double) double "fmod"))
|
|
]
|
|
|
|
The @scheme[c-declare] form can also be used to define helper C
|
|
functions to be called through @scheme[c-lambda].
|
|
|
|
The @scheme[c-include] form expands to a @scheme[c-declare] form using
|
|
the content of a specified file. Use @scheme[(c-include _file)] instead
|
|
of @scheme[(c-declare "#include @scheme[_file]")] when it's easier to
|
|
have MzScheme resolve the file path than to have the C compiler
|
|
resolve it.
|
|
|
|
The @filepath{plt/collects/mzscheme/examples} directory in the PLT
|
|
distribution contains additional examples.
|
|
|
|
When compiling for MzScheme3m (see @|inside|), C code inserted by
|
|
@scheme[c-lambda], @scheme[c-declare], and @scheme[c-include] will be
|
|
transformed in the same was as @|mzc|'s @DFlag{xform} mode (which may
|
|
or may not be enough to make the code work correctly in MzScheme3m;
|
|
see @|inside| for more information).
|
|
|
|
|
|
@defform[(c-lambda (argument-type ...) return-type impl-string ...+)]{
|
|
|
|
Creates a Scheme procedure whose body is implemented in C. The
|
|
procedure takes as many arguments as the supplied
|
|
@scheme[argument-type]s, and it returns one value. If
|
|
@scheme[return-type] is @schemeidfont{void}, the procedure's result is
|
|
always void. The @scheme[impl-string] is either the name of a C
|
|
function (or macro) or the body of a C function.
|
|
|
|
If a single @scheme[impl-string] is provided, and if it is a
|
|
string containing only alphanumeric characters and @litchar{_},
|
|
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 @scheme[argument-type] (as
|
|
described below) to produce an argument to the C function. Unless
|
|
@scheme[return-type] is @schemeidfont{void}, the C function's result is
|
|
converted according to @scheme[return-type] for the Scheme
|
|
procedure's result.
|
|
|
|
If more than @scheme[impl-string] is provided, or if it contains more
|
|
than alphanumeric characters and @litchar{_}, then the concatenated
|
|
@scheme[impl-string]s must contain C code to implement the function
|
|
body. The converted arguments for the function will be in variables
|
|
@cpp{___arg1}, @cpp{___arg2}, ... (with three underscores in each
|
|
name) in the context where the @scheme[impl-string]s are placed for
|
|
compilation. Unless @scheme[return-type] is @schemeidfont{void}, the
|
|
@scheme[impl-string]s code should assign a result to the variable
|
|
@cpp{___result} (three underscores), which will be declared but not
|
|
initialized. The @scheme[impl-string]s code should not return
|
|
explicitly; control should always reach the end of the body. If the
|
|
@scheme[impl-string]s code defines the pre-processor macro
|
|
@cpp{___AT_END} (with three leading underscores), then the macro's
|
|
value should be C code to execute after the value @cpp{___result} is
|
|
converted to a Scheme result, but before the result is returned, all
|
|
in the same block; defining @cpp{___AT_END} is primarily useful for
|
|
deallocating a string in @cpp{___result} that has been copied by
|
|
conversion. The @scheme[impl-string]s code will start on a new line at
|
|
the beginning of a block in its compilation context, and
|
|
@cpp{___AT_END} will be undefined after the code.
|
|
|
|
In addition to @cpp{___arg1}, etc., the variable @cpp{argc} is bound
|
|
in @scheme[impl-string]s to the number of arguments supplied to
|
|
the function, and @cpp{argv} is bound to a @cpp{Scheme_Object*} array
|
|
of length @cpp{argc} containing the function arguments as Scheme
|
|
values. The @cpp{argv} and @cpp{argc} variables are mainly useful for
|
|
error reporting (e.g., with @cpp{scheme_wrong_type}).
|
|
|
|
Each @scheme[argument-type] must be one of the following, which are
|
|
recognized symbolically:
|
|
|
|
@itemize{
|
|
|
|
@argtype[@scheme[bool] "any value" @cpp{int}
|
|
@toC{@scheme[#f] @|goesto| 0, anything else @|goesto| 1}
|
|
@toScheme{0 @|goesto| @scheme[#f], anything else @|goesto| @scheme[#t]}]
|
|
|
|
@argtype[@scheme[char] "character" @cpp{char}
|
|
@toC{character's Latin-1 value cast to signed byte}
|
|
@toScheme{Latin-1 value from unsigned cast mapped to character}]
|
|
|
|
@argtype[@scheme[unsigned-char] "character" @cpp{unsigned char}
|
|
@toC{character's Latin-1 value}
|
|
@toScheme{Latin-1 value mapped to character}]
|
|
|
|
@argtype[@scheme[signed-char] "character" @cpp{signed char}
|
|
@toC{character's Latin-1 value cast to signed byte}
|
|
@toScheme{Latin-1 value from unsigned cast mapped to character}]
|
|
|
|
@argtype[@scheme[int] @elem{exact integer that fits into an @cpp{int}} @cpp{int}
|
|
@tosObvious]
|
|
|
|
@argtype[@scheme[unsigned-int] @elem{exact integer that fits into an @cpp{unsigned int}} @cpp{unsigned int}
|
|
@tosObvious]
|
|
|
|
@argtype[@scheme[long] @elem{exact integer that fits into a @cpp{long}} @cpp{long}
|
|
@tosObvious]
|
|
|
|
@argtype[@scheme[unsigned-long] @elem{exact integer that fits into an @cpp{unsigned long}} @cpp{unsigned long}
|
|
@tosObvious]
|
|
|
|
@argtype[@scheme[short] @elem{exact integer that fits into a @cpp{short}} @cpp{short}
|
|
@tosObvious]
|
|
|
|
@argtype[@scheme[unsigned-short] @elem{exact integer that fits into an @cpp{unsigned short}} @cpp{unsigned short}
|
|
@tosObvious]
|
|
|
|
@argtype[@scheme[float] "real number" @cpp{float}
|
|
@toC{number converted to inexact and cast to @cpp{float}}
|
|
@toScheme{cast to @cpp{double} and encapsulated as an inexact number}]
|
|
|
|
@argtype[@scheme[double] "real number" @cpp{double}
|
|
@toC{number converted to inexact}
|
|
@toScheme{encapsulated as an inexact number}]
|
|
|
|
@argtype[@scheme[char-string] @elem{byte string or @scheme[#f]} @cpp{char*}
|
|
@toC{string @|goesto| contained byte-array pointer, @scheme[#f] @|goesto| @cpp{NULL}}
|
|
@toScheme{@cpp{NULL} @|goesto| @scheme[#f], anything else @|goesto| new byte string created by copying the string}]
|
|
|
|
@argtype[@scheme[nonnull-char-string] "byte string" @cpp{char*}
|
|
@toC{byte string's contained byte-array pointer}
|
|
@toScheme{new byte string created by copying the string}]
|
|
|
|
@argtype[@scheme[scheme-object] "any value" @cpp{Scheme_Object*}
|
|
@toC{no conversion}
|
|
@toScheme{no conversion}]
|
|
|
|
@argtype[@scheme[(pointer _bstr)] @elem{an opaque c-pointer value, identified as type @scheme[bstr], or @scheme[#f]} @cpp{@scheme[_bstr]*}
|
|
@toC{@scheme[#f] @|goesto| @cpp{NULL}, c-pointer @|goesto| contained pointer cast to @cpp{@scheme[_bstr]*}}
|
|
@toScheme{@cpp{NULL} @|goesto| @scheme[#f], anything else @|goesto| new c-pointer containing the pointer
|
|
and identified as type @scheme[_bstr]}]
|
|
|
|
}
|
|
|
|
The @scheme[return-type] must be @schemeidfont{void} or one of the
|
|
@scheme[arg-type] keywords.}
|
|
|
|
@defform[(c-declare code-string)]{
|
|
|
|
Declares arbitrary C code to appear after @filepath{escheme.h} or
|
|
@filepath{scheme.h} is included, but before any other code in the
|
|
compilation environment of the declaration. A @scheme[c-declare] form
|
|
can appear only at the top-level or within a module's top-level
|
|
sequence.
|
|
|
|
The @scheme[code] code will appear on a new line in the file for C
|
|
compilation. Multiple @scheme[c-include] declarations are concatenated
|
|
(with newlines) in order to produces a sequence of declarations.}
|
|
|
|
@defform[(c-include path-spec)]{
|
|
|
|
Expands to a use of @scheme[c-declare] with the content of
|
|
@scheme[path-spec]. The @scheme[path-spec] has the same form as for
|
|
@schememodname[mzlib/include]'s @scheme[include].}
|
|
|
|
|
|
@(bibliography
|
|
(bib-entry
|
|
#:key "Feeley98"
|
|
#:author "Marc Feeley"
|
|
#:title "Gambit-C, version 3.0"
|
|
#:date "1998"))
|