gauche.ffi - Foreign function interface ¶This module provides the means to call compiled functions in other languages via C ABI. It allows user programs to use external libraries without writing a custom extension.
Although it is convenient, you need to keep in mind that the external libraries does not necessarily aligns Gauche’s runtime conventions, therefore you cannot rely on Gauche’s guardrails. You can easily pass arguments of incorrect types, interpret the result incorrectly, or access objects outside of its lifetime. Small mistakes can eaisly cause access violation or memory corruption.
We adopt pluggable subsystems; we have a few different FFI implementations. The system chooses one suitable for the situation, or you can specify which one ot use. Here is the list of available subsystems:
:nativeA machine-code trampoline stub is constructed at the runtime. The overhad is small, but this is architecture-dependent and only available on limited architecture (currently, x86_64 on Unix-family and MinGW. a plan to support Aarch64 in near future). This is selected by default when you’re running on the supported platform.
:stubA stub C code is generated, compiled and dyamically linked when
with-ffi form is evaluated. It incurs one-time overhead when
the compilation takes place; but once linked, calling the FFI functions
are just as fast as calling C funcitons via extension modules.
This is available on all platforms, and used as the fallback by default.
:aotA variation of :stub subsystem, used when the file including
with-ffi forms are precompiled. The stub code generation is
done at the same time of precompilation, so loading the compiled module
does not incur extra overhead. This is selected when
you precompile the file, regardless of the underlying architecture.
The code that uses FFI looks like the following:
(use gauche.ffi) (use gauche.native-type) (with-ffi (dlopen "/path/to/library") () ;; Define C functions and callbacks (define-c-function foo '(int c-string) 'int) ... ) ;; Call the defined C function (foo 4 "bar")
Foreign functions are defined within the form ‘with-ffi‘. Inside the form, you can define C functions and C callbacks. The defined C functions are visible from the rest of the code, and you can call it as a Scheme function.
| • With-ffi form: | ||
| • FFI support procedures: |
with-ffi form ¶{gauche.ffi}
The dso form must be an expression that yields a dynamically
loaded object (dlopen, see FFI support procedures),
options is a keyword-value form to tune the behavior
of with-ffi, and form is a toplevel form
to define foreign interface (see below) which accesses the external
function provided by dso.
dso can be #f if form … only refer to
the values solely derived from the header files (you need to
specify :c-headers option in that case).
You can mix ordinary toplevel definitions like defin in
forms. However, with-ffi may reorder forms
depending on its kind.
This form does not create a scope; the toplevel definitions
of form … are spliced into the surrounding scope.
Some subsystem may need to insert the binding into the module’s
toplevel scope, so we recommend users to place with-ffi form
at the toplevel scope as well, to avoid the confusion.
:subsystem nameSpecify the subsystem to use. Available names are one of
:native, :stub, or :aot.
See gauche.ffi - Foreign function interface, for the description of
each subsystem.
Without this option, the most appropriate subsystem is automatically selected. You need this only in special circumstances, such as working around issues in subsystems.
:c-headers (header-name …)Specify a list of headers of the library.
If you only use C functions and C callbacks with standard
types, you don’t
need this. On the other hand, if you use C constants
(#defined macro constants) or C enums,
you need to specify this, as their information cannot
be extracted from the DSO.
You may also need headers if you use c opaque pointers
(see Derived and aggregate native types), as the opaque type
name needs to be visible from the C compiler when the :stub
or :aot subsystem is used. In case if you don’t have a header,
however, you can workaround with setting c-name to "void";
see make-c-opaque-pointer-type for the details.
{gauche.ffi}
Bind name to a Scheme procedure that calls C function
with the name derived by cgen-safe-name-friendly from
name. Basically,
hyphens in name becomes underbar in C name, e.g.
you can call C function gtk_init with Scheme name
gtk-init. See Generating C source files, for
more detailed transliteration rules.
As a special case, if name begins with %,
the character is removed to derive the C name. It is useful
when you want to expose the Scheme function which is a wrapper
of C function for convenience. That is, you can define FFI to
a C function foo with (define-c-function %foo ...),
then define a wrapper Scheme function foo, which wraps
%fpp, as an external API.
arg-types is a list of native type signatures to
specify types of the arguments, and return-type is a
native type signature.
See Native type signature), for the details of native
type signatue. The last element of arg-types can be
a symbol ..., to indicate the C function takes varargs.
Both arg-types and return-type are evaluated,
which allows you to mix expressoins that yields a native
type in them.
For example, a C function foo that takes an int
and a pointer to int, and returns double, can be
described as follows. Note the quotes for arg-types
and return-type, as they are evaluated:
(define-c-function foo '(int int*) 'double)
Or, you can use native type objects.
(define-c-function foo `(,<int> ,(make-c-pointer-type <int>)) <double>) ;; You can mix native types and literal native type signatures as well: (define-c-function foo `(,<int> int*) <double>)
It is useful when you defines custom types and use them for arguments/return types.
{gauche.ffi}
{gauche.ffi}
This form defines a Scheme constant name, bound to a
#define-d constant of the same name in the C library.
In order to use this form,
you have to provide the library’s header file to :c-headers
option in with-ffi form, because #define-d constants
cannot be extracted from the compiled binary.
By default, the constant is assumed of type <fixnum>. If the
defined constant is of other types, you need to provide a native type
object or native type signature to type
(see Native type signature).
An error is signaled if name is not defined in C.
The same effect can be achieved by bindng name to an explicit
literal constant with define-constant. If you do so, however,
you have to change the literal value whenever a new revision of C API
changes the value. Also, it would be cumbersome if different platforms
use different values.
NB: Currently, this form does not work with the :native subsystem.
If you use this form in with-ffi without specifying the subsystem,
the :stub subsystem is used for the with-ffi form
even if the platform supports :native subsystem.
{gauche.ffi}
This form defines Scheme constants for C enum values, and also binds
name to a C-enum native type.
With the first form, name is also used as the enum tag name,
modulo transliterations similar to define-c-function (e.g.
- to _, etc.) To specify a different tag name,
you can use the second form. If you want to specify no tags
(i.e. anonymous enum), you can give #f to tag.
Each enumerator is a symbol, corresponding to the C’s enumerators. The value of each enumerator is taken from the C definition, so its order doesn’t matter, and it is allowed to list only a subset of enumerators. However, an error is signaled if the name in enumderator isn’t defined in C.
The following example reflects one of Gauche’s enums into Scheme:
(with-ffi #f (:c-headers ("gauche.h"))
(define-c-enum (cmp-mode #f)
(SCM_CMP_EQ
SCM_CMP_EQV
SCM_CMP_EQUAL))
It binds Scheme constants SCM_CMP_EQ, SCM_CMP_EQV,
and SCM_CMP_EQUAL to the appropriate values defined in
gauche.h, and also binds cmp-mode to a native
c-enum type, which can be used to convert symbols and values
(c-enum-symbol and c-enum-value,
see Derived and aggregate native types).
The same effect can be achieved by manually defining c-enum type and constants:
(define-type cmp-mode (native-type '(.enum (SCM_CMP_EQ SCM_CMP_EQV SCM_CMP_EQUAL)))) (define-constant SCM_CMP_EQ 0) (define-constant SCM_CMP_EQV 1) (define-constant SCM_CMP_EQUAL 2)
If you do so, however, you need to keep the values in sync with the C API manually.
It is up to the C compiler to choose which integer type to be used
to store the enum value. It matters when you define a C struct type with
a field of the enum type, because it affects the layout of the struct.
We try to guess the appropriate type as much as possible, with
<uint32> as the fallback value.
If it is not what you want, you can give
a native type object or native type signatire to the optional
base-type argument.
NB: Currently, this form does not work with the :native subsystem.
If you use this form in with-ffi without specifying the subsystem,
the :stub subsystem is used for the with-ffi form
even if the platform supports :native subsystem.
{gauche.ffi}
This is a convenience procedure, and simply calls
dynamic-load with :init-function #f—that is,
telling the dynamic loader not to call Gauche-specific initialization function.
We find it worth to have this shortcut, because it aligns to the
programmer’s expectation.
Returns #<dlobj> object, which can be given to with-ffi
form.
The keyword arguments are passed to dynamic-load.
Paths specifies the list of directories to search dsoname
if it is given in a relative path—when omitted, the value of
the parameter dynamic-load-paths is used.
Versions specifies a list of version suffixes, each of
whose element may be an integer or a string. The version number is
attached after DSO suffix. See Load dynamic library, for
the details.
{gauche.ffi}
{gauche.ffi}
{gauche.ffi}
Returns a native pointer to a memory region allocated by system’s malloc.
The memory is not subject to Gauche’s GC; you need to either free
it by yourself, using native-free, or pass the pointer to
a foreign function which will be responsible for freeing.
It is mainly for foreign functions that take the owneship of the
objects whose pointer is passed. You should not pass a native handle
pointing Gauche objects to such functions, since calling free on
those objects will be a disaster.
The size-or-type argument must be either nonnegative exact integer
that specifies the size of the memory in bytes to allocate,
or a <native-type> object, whose size is allocated.
If a size is given, the returned handle has a type <void*>.
If a native type is given, the returned handle has a pointer to that type.
{gauche.ffi}
handle must be a native handle pointing a memory allocated by
the system’s malloc. It can be the one created by native-alloc,
or the one which is returned by a foreign function and whose freeing
is caller’s responsibility.