32.3.1. Introduction

This facility, also known as “Foreign Language Interface”,
allows one to call a function implemented in C from inside CLISP
and to do many related things, like inspect and modify foreign memory,
define a “callback” (i.e., make a lisp function available
to the C world), etc.
To use this facility, one writes a foreign function
description into an
ordinary Lisp file, which is then compiled and loaded as usual;
or just evaluates the appropriate form in the read-eval-print loop.

Unfortunately, this functionality is not available on some
operating systems, and, also, it offers only a part of the foreign
functionality: cpp macros and inline functions
cannot be accessed this way. On the other hand, this functionality
is available in the read-eval-print loop and does not require a C compiler.

Use a somewhat less direct way: when you do not use
the :LIBRARY argument, COMPILE-FILE produces a #P".c" file
(in addition to a #P".fas" and a #P".lib").
Then you compile (with a C compiler) and link it into CLISP
(statically, linking it into lisp.a, or
dynamically, loading it into a running CLISP using dlopen and dlsym).
This way you can use any functionality your foreign library exports,
whether using ordinary functions, inline functions,
or cpp macros (see Example 32.6, “Accessing cpp macros”).

All symbols relating to the foreign function interface are
exported from the package “FFI”.
To use them, (USE-PACKAGE“FFI”).

This option can be either :NONE or
:MALLOC-FREE and defaults to :NONE. If it is
:MALLOC-FREE, any values of type FFI:C-STRING, FFI:C-PTR,
FFI:C-PTR-NULL, FFI:C-ARRAY-PTR within the foreign value are assumed
to be pointers to malloc-allocated storage, and when SETQ
replaces an old value by a new one, the old storage is freed using
free and the new storage allocated using malloc. If it is
:NONE, SETQ assumes that the pointers point to good storage
(not NULL!) and overwrites the old values by the new ones.
This is dangerous (just think of overwriting a string with a
longer one or storing some data in a NULL pointer...) and
deprecated.

Alas, this would work in the current
image only: if you save the
image, GSL_CHEB_ALLOC will not work there
because CLISP will try to re-open libgsl.so and
fail as above. However, using the :REQUIRE argument
will tell CLISP to re-open both libraries in the right order:

The default is set separately in each compilation unit, so, if you
are interfacing to a single library, you can set this variable in the
beginning of your lisp file and omit the :LIBRARY argument
throughout the file.

This form defines name to be both a
STRUCTURE-CLASS and a foreign C type with the given slots.
If this class representation overhead is not needed one should consider
writing (FFI:DEF-C-TYPEname (FFI:C-STRUCT
{LIST | VECTOR} (symbolc-type)*)) instead.
name is a SYMBOL (structure name) or a LIST whose FIRST
element is the structure name and the REST is options.
Two options are supported at this time:

You can use (FFI:ENUM-FROM-VALUEnamevalue) and
(FFI:ENUM-TO-VALUEnamesymbol) to convert between the numeric and symbolic
representations (of course, the latter function boils down to
SYMBOL-VALUE plus a check that the symbol is indeed a constant
defined in the FFI:DEF-C-ENUMname).

Array element: If c-place is of foreign type
(FFI:C-ARRAYc-type (dim1 ... dimn))
and 0 ≤ index1 < dim1, ..., 0 ≤ indexn < dimn,
this will be the place corresponding to (AREFc-placeindex1 ... indexn) or
c-place[index1]...[indexn].
It is a place of type c-type.
If c-place is of foreign type (FFI:C-ARRAY-MAXc-typedim) and 0 ≤ index < dim,
this will be the place corresponding to (AREFc-placeindex) or c-place[index].
It is a place of type c-type.

Type change and displacement: return a place denoting
a memory locations displaced from the original c-place by an
offset counted in bytes, with type c-type.
This can be used to resize an array, e.g. of c-type(FFI:C-ARRAYuint16n)
via (FFI:OFFSETc-place 0 '(FFI:C-ARRAYuint16k)).

This predicate returns NIL if the foreign-entity
(e.g. the Lisp equivalent of a FFI:C-POINTER) refers to a pointer
which is invalid (e.g., because it comes from a previous Lisp session).
It returns T if foreign-entity can be used within the current Lisp process
(thus it returns T for all non-foreign arguments).

You can invalidate a foreign object using
(SETFFFI:VALIDP).
You cannot resurrect a zombie, nor can you kill a non-foreign object.

When initarg is not supplied,
they allocate space only for (FFI:SIZEOFc-type) bytes.
This space is filled with zeroes. E.g.,
using a c-type of FFI:C-STRING or even (FFI:C-PTR
(FFI:C-ARRAYuint8 32)) (!) both allocate space
for a single pointer, initialized to NULL.

When initarg is supplied, they
allocate space for an arbitrarily complex set of structures rooted in
c-type. Therefore, FFI:C-ARRAY-MAX, #()
and "" are your friends for creating a
pointer to the empty arrays:

body is then executed with the three variables foreign-address,
char-count and
byte-count respectively bound to an
untyped FFI:FOREIGN-ADDRESS (as known from the FFI:C-POINTER foreign
type specification) pointing to the stack location, the number of
CHARACTERs of the Lisp string that were considered and the
number of (UNSIGNED-BYTE 8) bytes that were allocated for it on the C
stack.

When null-terminated-p is true,
which is the default, a variable number of zero bytes is appended,
depending on the encoding, e.g. 2 for CHARSET:UTF-16,
and accounted for in byte-count,
and char-count is incremented by one.

The FFI:FOREIGN-ADDRESS object bound to foreign-address is
invalidated upon the exit from the form.

Note

Although you can memoize a c-type-internal (see
Section 31.11.3, “Macro EXT:MEMOIZED” - but do not expect type redefinitions to
work across memoization!), you cannot serialize it (write to
disk) because deserialization loses object identity.

Macro FFI:ALLOCATE-SHALLOW allocates
(FFI:SIZEOFc-type)
bytes on the C heap and zeroes them out
(like calloc).
When :COUNT is supplied, c-type is substituted with
(FFI:C-ARRAYc-typecount),
except when c-type is CHARACTER, in which case
(FFI:C-ARRAY-MAXCHARACTERcount)
is used instead.
When :READ-ONLY is supplied, the Lisp side is prevented from modifying the
memory contents. This can be used as an indication that some foreign
side is going to fill this memory (e.g. via read).

Returns a FFI:FOREIGN-VARIABLE object of the actual c-type,
whose address part points to the newly allocated memory.

FFI:ALLOCATE-DEEP will call Cmalloc as many times
as necessary to build a structure on the C heap of the given
c-type, initialized from the given contents.

Function FFI:FOREIGN-FREE deallocates memory at the address
held by the given foreign-entity. If :FULL is supplied
and the argument is of type FFI:FOREIGN-VARIABLE, recursively frees
the whole complex structure pointed to by this variable.

If given a FFI:FOREIGN-FUNCTION object that corresponds to a
CLISP callback, deallocates it. Callbacks are automatically
created each time you pass a Lisp function via the “FFI”.

Use (SETFFFI:VALIDP) to disable further
references to this address from Lisp. This is currently not done
automatically. If the given pointer is already invalid,
FFI:FOREIGN-FREE (currently) SIGNALs an ERROR. This may change to
make it easier to integrate with EXT:FINALIZE.

CLISP will write the extern
declarations for foreign functions (defined with FFI:DEF-CALL-OUT) and
foreign variables (defined with FFI:DEF-C-VAR) into the output #P".c"
(when the Lisp file is compiled with COMPILE-FILE)
unless these variables are NIL.
They are NIL by default, so the extern
declarations are not written; you are encouraged to use
FFI:C-LINES to include the appropriate C headers.
Set these variables to non-NIL if the headers are not available or
not usable.

This type corresponds to what C calls
void*, an opaque pointer.
When used as an argument, NIL is accepted as a FFI:C-POINTER and
treated as NULL; when a function wants to return a NULLFFI:C-POINTER, it actually returns NIL.

This type is equivalent to what C calls
c-type *: a pointer to a single item of the given
c-type. It differs from (FFI:C-PTR-NULLc-type) (see below) in that no conversion to and from
Lisp will occur (beyond the usual one of the CNULL pointer
to or from Lisp NIL). Instead, an object of type FFI:FOREIGN-VARIABLE
is used to represent the foreign place. It is assimilable to a typed
pointer.

This type is equivalent to what C calls
struct { c-type1ident1; ...; c-typenidentn; }.
Its Lisp equivalent is: if class is VECTOR, a
SIMPLE-VECTOR; if class is LIST, a proper list;
if class is a symbol naming a structure or CLOS class, an
instance of this class, with slots of names
ident1, ..., identn.

This type is equivalent to what C calls
c-type [dim1] ... [dimn].
Note that when an array is passed as an argument to a function in
C, it is actually passed as a pointer; you therefore have to
write (FFI:C-PTR (FFI:C-ARRAY ...)) for this
argument's type.

This type is equivalent to what C calls
c-type [maxdimension], an array containing up to
maxdimension elements.
The array is zero-terminated if it contains less than maxdimension elements.
Conversion from Lisp of an array with more than maxdimension elements
silently ignores the extra elements.

This type designates a C function that can be
called according to the given prototype
(r-c-type (*)
(a-c-type1, ...)).
Conversion between C functions and Lisp functions
is transparent, and NULL/NIL is recognized and
accepted.

The default language is set using the macro
FFI:DEFAULT-FOREIGN-LANGUAGE.
If this macro has not been called in the current compilation unit
(usually a file), a warning is issued and :STDC is used for the rest
of the unit.

32.3.5. Foreign variables

Foreign variables are variables whose
storage is allocated in the foreign language module.
They can nevertheless be evaluated and modified through SETQ,
just as normal variables can, except that the range of allowed values
is limited according to the variable's foreign type.

32.3.7. Foreign functions

Foreign functions are functions which are defined in the foreign language.
There are named foreign functions
(imported via FFI:DEF-CALL-OUT or created via FFI:DEF-CALL-IN) and
anonymous foreign functions; they arise through conversion of function
pointers using FFI:FOREIGN-FUNCTION.

A call-out function is a foreign function
called from Lisp: control flow temporarily leaves Lisp.
A call-in function
(AKA callback)
is a Lisp function called from the foreign language:
control flow temporary enters Lisp.

32.3.7.1. Callbacks and memory management

Callbacks (C function calling Lisp function) create
so-called trampolines.
A trampoline is a piece of C code which knows how to call a
particular Lisp function.
(That is how all foreign language interfaces work, not just ours).
The C pointer that the foreign library receives is the pointer
to this piece of code.
These are not subject to garbage-collection, as there is no protocol to
tell the garbage collector when a given callback is not needed anymore
(unlike with Lisp objects).

With callbacks to named functions (i.e.,
created by a FFI:DEF-CALL-IN form where function is a function name) this is
mostly harmless, since function is unlikely to be redefined.

With callbacks to anonymous functions (i.e.,
created by FFI:FOREIGN-FUNCTION or a FFI:DEF-CALL-IN form where function
argument is a lambda expression), this might become an issue when they are
produced dynamically and en masse, e.g. inside a loop, so that many
trampolines are generated.

32.3.9. Parameter Mode

The callee passes information back to the caller on
return. When viewed as a Lisp function, there is no Lisp argument
corresponding to this, instead it means an additional return value.
Requires ALLOCATION = :ALLOCA.

:IN-OUT (means: read-write):

Information is passed from the caller to the callee
and then back to the caller. When viewed as a Lisp function, the
:OUT value is returned as an additional return value.

To sort an array of double-floats using the Lisp function SORT
instead of the C library function
qsort, one can use the
following interface code sort1.c.
The main problem is to pass a variable-sized array.

follows a typical pattern of C“out”-parameter
convention: it expects a pointer to a buffer it is going to fill.
So you must view this parameter as either :OUT or :IN-OUT.
Additionally, one must tell the function the size of the buffer.
Here length is just an :IN parameter.
Sometimes this will be an :IN-OUT parameter, returning the
number of bytes actually filled in.

So name is actually a pointer to an array of up to length
characters, regardless of what the poor char*C
prototype says, to be used like a Cstring
(NULL-termination). UNIX specifies that “host names are
limited to HOST_NAME_MAX bytes”, which is,
of course, system dependent, but it appears that 256 is sufficient.

In the present example, you can use allocation :ALLOCA, like
you would do in C: stack-allocate a temporary:

Now we have a different problem:
if gethostname fails, then the buffer
allocated for name will be filled with garbage, but it will still go
through the string conversion before we can check
the success status.
If CUSTOM:*FOREIGN-ENCODING* is CHARSET:ISO-8859-1, this is not a problem since no real
conversion is happening, but with CHARSET:UTF-8 an ERROR may be SIGNALed.
A safe approach is to pass to the foreign function our own
stack-allocated buffer, and only convert the buffer to a string when the
foreign function succeeds: