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.
There are two basic ways to do define a foreign function:
dlopen
and
dlsym
to get to the location of the
function code in a dynamic library.
To access this facility, pass the :LIBRARY
option to FFI:DEF-CALL-OUT
and FFI:DEF-C-VAR
.
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.: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 31.5, “Accessing cpp macros”).
All symbols relating to the foreign function interface are
exported from the package “FFI”. To use them,
(
.USE-PACKAGE
“FFI”)
Special “FFI” forms may appear anywhere in the Lisp file.
These are the special “FFI” forms. We have taken a pragmatic approach: the only foreign languages we support for now are C and ANSI C.
Unless specifically noted otherwise, type specification
parameters are not evaluated, so that they can be compiled by
FFI:PARSE-C-TYPE
into the internal format at macroexpansion time.
High-level “FFI” forms; name
is any Lisp
SYMBOL
; c-name
is a STRING
(FFI:DEF-C-TYPE
name
c-type
)
name
a shortcut for c-type
.
Note that c-type
may already refer to name.
Forward declarations of types are not possible, however.
(FFI:DEF-C-VAR
name
{option
}*)
This form defines a FFI:FOREIGN-VARIABLE
.
name
is the Lisp name, a regular Lisp SYMBOL
.
Options for FFI:DEF-C-VAR
(:NAME
c-name
)
STRING
. If not specified, it is derived from the print name of
the Lisp name.(:TYPE
c-type
)
(:READ-ONLY
BOOLEAN
)
NIL
,
it will be impossible to change the variable's value from within
Lisp (using SETQ
or similar).(:ALLOC ALLOCATION
)
: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.(:LIBRARY
name
)
FFI:DEFAULT-FOREIGN-LIBRARY
.(:DOCUMENTATION
string
)
VARIABLE
documentation.
(FFI:DEF-C-CONST
name
{option
}*)
This form defines a Lisp constant variable name
whose
value is determined at link time using an internal FFI:FOREIGN-FUNCTION
.
When the cpp constant is not defined, name
is unbound.
Options for FFI:DEF-C-CONST
(:NAME
c-name
)
STRING
. If not specified, it is derived from the print name
of the Lisp name.(:TYPE
c-type
)
specifies the constant's foreign type, one of
FFI:INT |
FFI:C-STRING |
FFI:C-POINTER |
(:DOCUMENTATION
string
)
VARIABLE
documentation.
See also Example 31.5, “Accessing cpp macros”.
(FFI:DEF-CALL-OUT
name
{option
}*)
This form defines a named call-out function (a foreign function called from Lisp: control flow temporarily leaves Lisp).
Options for FFI:DEF-CALL-OUT
(:NAME
c-name
)
#'name
is redirected to call the C function c-name
.
(:ARGUMENTS
{(argument
c-type
[PARAM-MODE
[ALLOCATION
]])}*)
(:RETURN-TYPE
c-type
[ALLOCATION
])
(:LANGUAGE
language
)
(:BUILT-IN BOOLEAN
)
FFI:*OUTPUT-C-FUNCTIONS*
).
(:LIBRARY
name
)
FFI:DEFAULT-FOREIGN-LIBRARY
(:DOCUMENTATION
string
)
FUNCTION
documentation.
(FFI:DEF-CALL-IN
name
{option
}*)
This form defines a named call-in function (i.e., a Lisp function called from the foreign language: control flow temporary enters Lisp)
Options for FFI:DEF-CALL-IN
(:NAME
c-name
)
c-name
is redirected to call the Common Lisp function
#'name
.(:ARGUMENTS
{(argument
c-type
[PARAM-MODE
[ALLOCATION
]])}*)
(:RETURN-TYPE
c-type
[ALLOCATION
])
(:LANGUAGE
language
)
(FFI:CLOSE-FOREIGN-LIBRARY
name
)
Close (unload) a shared foreign library (opened by the
:LIBRARY
argument to FFI:DEF-CALL-OUT
or FFI:DEF-C-VAR
).
If you modify your shared library, you need to use close it
using FFI:CLOSE-FOREIGN-LIBRARY
first. When you try to use the
FFI:FOREIGN-VARIABLE
or the FFI:FOREIGN-FUNCTION
which resides in the
library name
, it will be re-opened automatically.
(FFI:DEFAULT-FOREIGN-LIBRARY
library-name
)
This macro sets the default :LIBRARY
argument for
FFI:DEF-CALL-OUT
and FFI:DEF-C-VAR
. library-name
should be NIL
(meaning use the C file produced by COMPILE-FILE
), a
STRING
, or, depending on the
underlying dlsym
implementation,
:DEFAULT
or :NEXT
.
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.
(FFI:DEF-C-STRUCT
name
(symbol
c-type
)*)
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 (
instead.
FFI:DEF-C-TYPE
name
(FFI:C-STRUCT
{LIST
| VECTOR
} (symbol
c-type
)*))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:
Options for FFI:DEF-C-STRUCT
:TYPEDEF
typedef
elsewhere.:EXTERNAL
#P".c"
file that you include with, e.g.,
(FFI:C-LINES
"#include <filename.h>~%")
.
These options determine how the struct is written to the #P".c"
.
(FFI:DEF-C-ENUM
name
{symbol
| (symbol
[value
])}*)
This form defines symbol
s
as constants, similarly to the C declaration enum {
symbol
[= value
], ... };
You can use (
and
FFI:ENUM-FROM-VALUE
name
value
)(
to convert between the numeric and symbolic
representations (of course, the latter function boils down to
FFI:ENUM-TO-VALUE
name
symbol
)SYMBOL-VALUE
plus a check that the symbol
is indeed a constant
defined in the FFI:DEF-C-ENUM
name
).
(FFI:C-LINES
format-string
{argument
}*)
This form outputs the string
(
to the C output file's top level.
This is usually used to include the relevant header files,
see FORMAT
NIL
format-string
{argument
}*):EXTERNAL
and FFI:*OUTPUT-C-FUNCTIONS*
.
When format-string
is not a STRING
, is should be a SYMBOL
,
and then the STRING
(
is added to the appropriate C function:FORMAT
NIL
{argument
}*)
:INIT-ALWAYS
:INIT-ONCE
:FINI
(FFI:ELEMENT
c-place
index1
...
indexn
)
c-place
is of foreign type
(FFI:C-ARRAY c-type
(dim1
... dimn
))
and 0 ≤ index1
< dim1
, ..., 0
≤ indexn
<
dimn
, this will be
the place corresponding to (AREF
c-place
index1
... indexn
)
or
c-place
[index1
]...[indexn
]
.
It is a place of type c-type
.
If c-place
is of foreign type (FFI:C-ARRAY-MAX
c-type
dim
)
and 0 ≤ index
< dim
,
this will be the place corresponding to (AREF
c-place
index
)
or c-place
[index
]
.
It is a place of type c-type
.
(FFI:DEREF
c-place
)
c-place
is of foreign type
(FFI:C-PTR c-type
)
,
(FFI:C-PTR-NULL c-type
)
or
(FFI:C-POINTER c-type
)
,
this will be the place the pointer points to.
It is a place of type c-type
.
For (FFI:C-PTR-NULL c-type
)
,
the c-place
may not be NULL
.
(FFI:SLOT
c-place
slot-name
)
c-place
is of
foreign type (FFI:C-STRUCT class
...
(slot-name
c-type
) ...)
or of
type (FFI:C-UNION
... (slot-name
c-type
) ...)
,
this will be of type c-type
.(FFI:CAST
c-place
c-type
)
c-place
, but of type c-type
.
(FFI:OFFSET
c-place
offset
c-type
)
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-ARRAY uint16 n
)
via (FFI:OFFSET
c-place
0 '(FFI:C-ARRAY uint16
k
))
.
(FFI:C-VAR-ADDRESS
c-place
)
c-place
as a Lisp object of
type FFI:FOREIGN-ADDRESS
. This is useful as an argument
to foreign functions expecting a parameter of C type FFI:C-POINTER.
(FFI:C-VAR-OBJECT
c-place
)
FFI:FOREIGN-VARIABLE
object underlying the
c-place
. This is also an acceptable argument type to a FFI:C-POINTER
declaration.(FFI:TYPEOF
c-place
)
c-type
corresponding to the c-place
.
(FFI:SIZEOF
c-type
)
(FFI:SIZEOF
c-place
)
The first form returns the size and alignment of the
C type c-type
, measured in bytes.
The second form returns the size and alignment of the
C type of c-place
, measured in bytes.
(FFI:BITSIZEOF
c-type
)
(FFI:BITSIZEOF
c-place
)
The first form returns the size and alignment of the
C type c-type
, measured in bits.
The second form returns the size and alignment of the
C type of c-place
, measured in bits.
(FFI:FOREIGN-ADDRESS-NULL
foreign-entity
)
T
if the
foreign-entity
refers to the NULL
address (and thus foreign-entity
should
probably not be passed to most foreign functions).
(FFI:FOREIGN-ADDRESS-UNSIGNED
foreign-entity
)
(FFI:UNSIGNED-FOREIGN-ADDRESS
number
)
FFI:FOREIGN-ADDRESS-UNSIGNED
returns the INTEGER
address embodied in the Lisp object of type FFI:FOREIGN-ADDRESS
,
FFI:FOREIGN-POINTER
, FFI:FOREIGN-VARIABLE
or FFI:FOREIGN-FUNCTION
.
FFI:UNSIGNED-FOREIGN-ADDRESS
returns a FFI:FOREIGN-ADDRESS
object pointing to the given INTEGER
address.
(FFI:FOREIGN-ADDRESS
foreign-entity
)
FFI:FOREIGN-ADDRESS
is both a type name and a
selector/constructor function. It is the Lisp object type
corresponding to a FFI:C-POINTER external type declaration, e.g. a
call-out function with (
yields
a Lisp object of type :RETURN-TYPE
FFI:C-POINTER)FFI:FOREIGN-ADDRESS
.
The function extracts the object of type FFI:FOREIGN-ADDRESS
living within any FFI:FOREIGN-VARIABLE
or FFI:FOREIGN-FUNCTION
object.
If the foreign-entity
already is a FFI:FOREIGN-ADDRESS
, it returns it.
If it is a FFI:FOREIGN-POINTER
(e.g. a base foreign library address),
it encapsulates it into a FFI:FOREIGN-ADDRESS
object, as suitable
for use with a FFI:C-POINTER external type declaration.
It does not construct addresses out of NUMBER
s,
FFI:UNSIGNED-FOREIGN-ADDRESS
must be used for that purpose.
(FFI:FOREIGN-VARIABLE
foreign-entity
c-type-internal
&KEY
name
)
FFI:FOREIGN-VARIABLE
from the given FFI:FOREIGN-ADDRESS
or FFI:FOREIGN-VARIABLE
and the
internal C type descriptor (as obtained from FFI:PARSE-C-TYPE
).
name
, a STRING
, is mostly useful for documentation and
interactive debugging since it appears in the printed representation
of the FFI:FOREIGN-VARIABLE
object, as in
#<FFI:FOREIGN-VARIABLE
"foo"
#x0ADD4E55>
.
In effect, this is similar to FFI:CAST
(or rather
(FFI:OFFSET
... 0 ...)
for places),
except that it works with FFI:FOREIGN-ADDRESS
objects and allows
caching of the internal C types.(FFI:FOREIGN-FUNCTION
foreign-entity
c-type-internal
&KEY
name
)
FFI:FOREIGN-FUNCTION
from the given FFI:FOREIGN-ADDRESS
or FFI:FOREIGN-FUNCTION
and the
internal C type descriptor (as obtained from
(FFI:PARSE-C-TYPE
'(FFI:C-FUNCTION ...))
,
in which case it is important to specify the :LANGUAGE
because the
expressions are likely to be evaluated at run time, outside the compilation unit).
name
, a STRING
, is mostly useful for documentation and
interactive debugging since it appears in the printed representation
of the FFI:FOREIGN-FUNCTION
object, as in
#<FFI:FOREIGN-FUNCTION
"foo"
#x0052B060>
.
It is inherited from the given FFI:FOREIGN-FUNCTION
object when
available.(FFI:VALIDP
foreign-entity
)
(SETF
(FFI:VALIDP
foreign-entity
) value
)
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
(
.
You cannot resurrect a zombie, nor can you kill a non-foreign object.
SETF
FFI:VALIDP
)
(FFI:FOREIGN-POINTER
foreign-entity
)
FFI:FOREIGN-POINTER
returns the FFI:FOREIGN-POINTER
associated with the Lisp object of type FFI:FOREIGN-ADDRESS
,
FFI:FOREIGN-POINTER
, FFI:FOREIGN-VARIABLE
or FFI:FOREIGN-FUNCTION
.
(FFI:SET-FOREIGN-POINTER
foreign-entity
{foreign-entity
|
:COPY
})
FFI:SET-FOREIGN-POINTER
changes the
FFI:FOREIGN-POINTER
associated with the Lisp object of type
FFI:FOREIGN-ADDRESS
, FFI:FOREIGN-VARIABLE
or FFI:FOREIGN-FUNCTION
to
that of the other entity.
With :COPY
, a fresh FFI:FOREIGN-POINTER
is allocated.
The original foreign-entity
still points to the same object and is returned.
This is particularly useful with (SETF
FFI:VALIDP
)
,
see Example 31.10, “Controlling validity of resources”.(FFI:WITH-FOREIGN-OBJECT
(variable
c-type
[initarg
]) body
)
(FFI:WITH-C-VAR
(variable
c-type
[initarg
]) body
)
These forms allocate space on the C execution
stack, bind respectively a FFI:FOREIGN-VARIABLE
object or
a local SYMBOL-MACRO
to variable
and execute body
.
When initarg
is not supplied,
they allocate space only for (
bytes. This space is filled with zeroes. E.g.,
using a FFI:SIZEOF
c-type
)c-type
of FFI:C-STRING or even (FFI:C-PTR
(FFI:C-ARRAY uint8 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:
(with-c-var (v '(c-ptr (c-array-max uint8 32)) #()) (setf (element (deref v) 0) 127) v)
c-type
is evaluated, making creation of variable sized buffers easy:
(with-c-var (fv `(c-array uint8 ,(length my-vector)) my-vector) (print fv))
(FFI:FOREIGN-VALUE
FFI:FOREIGN-VARIABLE
)
(SETF
(FFI:FOREIGN-VALUE
FFI:FOREIGN-VARIABLE
) ...)
This functions converts the reference to a C
data structure which the FFI:FOREIGN-VARIABLE
describes, to Lisp. Such a
reference is typically obtained from FFI:ALLOCATE-SHALLOW
,
FFI:ALLOCATE-DEEP
, FFI:FOREIGN-ALLOCATE
or via a (FFI:C-POINTER
C type description.
Alternatively, macros like c-type
)FFI:WITH-C-PLACE
or FFI:WITH-C-VAR
and the
concept of foreign place hide many uses of this function.
The SETF
form performs conversion from Lisp to C,
following to the FFI:FOREIGN-VARIABLE
's type description.
(FFI:WITH-FOREIGN-STRING
(foreign-address
char-count
byte-count
string
&KEY
encoding
null-terminated-p
start
end
) &BODY
body
)
This forms converts a Lisp string
according to
the encoding
, allocating space on the C execution stack.
encoding
can be any EXT:ENCODING
, e.g. CHARSET:UTF-16
or CHARSET:UTF-8
,
whereas CUSTOM:*FOREIGN-ENCODING*
must be an ASCII-compatible encoding.
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
CHARACTER
s of the Lisp string
that were considered and the
number of (
bytes that were allocated for it on the C
stack.UNSIGNED-BYTE
8)
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.
A stupid example (a quite costly interface
to mblen
):
(with-foreign-string (fv elems bytes string :encoding charset:jis... :null-terminated-p nil :end 5) (declare (ignore fv elems)) (format t "This string would take ~D bytes." bytes))
(FFI:PARSE-C-TYPE
c-type
)
(FFI:DEPARSE-C-TYPE
c-type-internal
)
Convert between the external (LIST
) and internal
(VECTOR
) C type representations (used by DESCRIBE
).
Although you can memoize a c-type-internal
(see
Section 30.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.
(FFI:ALLOCATE-SHALLOW
c-type
&KEY
:COUNT
:READ-ONLY
)
(FFI:ALLOCATE-DEEP
c-type
contents
&KEY
:COUNT
:READ-ONLY
)
(FFI:FOREIGN-FREE
foreign-entity
&KEY
:FULL)
(FFI:FOREIGN-ALLOCATE
c-type-internal
&KEY
:INITIAL-CONTENTS :COUNT
:READ-ONLY
)
Macro FFI:ALLOCATE-SHALLOW
allocates
(
bytes on the C heap and zeroes them out
(like FFI:SIZEOF
c-type
)calloc
).
When :COUNT
is supplied, c-type
is substituted with
(FFI:C-ARRAY
,
except when c-type
count
)c-type
is CHARACTER
, in which case
(FFI:C-ARRAY-MAX
is used instead.
When CHARACTER
count
):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 C malloc
as many times
as necessary to build a structure on the C heap of the given
c-type
, initialized from the given contents
.
E.g., (
performs 2 allocations: one for a C pointer to a string,
another for the contents of that string. This would be useful in
conjunction with a char** C type
declaration. FFI:ALLOCATE-DEEP
'FFI:C-STRING "ABCDE")(
allocates room for a single pointer (probably 4 bytes).FFI:ALLOCATE-SHALLOW
'FFI:C-STRING)
(
allocates and initializes room for the type FFI:ALLOCATE-DEEP
'CHARACTER
"ABCDEF" :count
10)(FFI:C-ARRAY-MAX
,
corresponding to char* or, more specifically,
char[10] in C.CHARACTER
10)
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 (
to disable further
references to this address from Lisp. This is currently not done
automatically. If the given pointer is already invalid,
SETF
FFI:VALIDP
)FFI:FOREIGN-FREE
(currently) SIGNAL
s an ERROR
. This may change to
make it easier to integrate with EXT:FINALIZE
.
Function FFI:FOREIGN-ALLOCATE
is a lower-level interface as it
requires an internal C type descriptor as returned by
FFI:PARSE-C-TYPE
.
(FFI:WITH-C-PLACE
(variable
foreign-entity
)
body
)
Create a place out of the given FFI:FOREIGN-VARIABLE
object so operations on places (e.g. FFI:CAST
, FFI:DEREF
, FFI:SLOT
etc.) can
be used within body
. FFI:WITH-C-VAR
appears as a composition of
FFI:WITH-FOREIGN-OBJECT
and FFI:WITH-C-PLACE
.
Such a place can be used to access memory referenced by a foreign-entity
object:
(setq foo (allocate-deep '(c-array uint8 3) rgb)) (with-c-place (place foo) (element place 0))
FFI:*OUTPUT-C-FUNCTIONS*
FFI:*OUTPUT-C-VARIABLES*
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.FFI:*FOREIGN-GUARD*
When this variable is non-NIL
at compile time,
CLISP will guard the C statements in the output file with
cpp conditionals to take advantage of GNU autoconf feature detection.
E.g.,
(eval-when (compile) (setq *foreign-guard* t)) (def-call-out some-function (:name "function_name") ...)
will produce
# if defined(HAVE_FUNCTION_NAME) register_foreign_function((void*)&function_name,"function_name",1024); # endif
and will compile and link on any system.
This is mostly useful for product delivery when you want your module to build on any system even if some features will not be available.
FFI:*FOREIGN-GUARD*
is initialized to NIL
for backwards compatibility.
Low-level “FFI” forms
(FFI:MEMORY-AS
foreign-address
c-type-internal
&OPTIONAL
offset
)
(SETF
(FFI:MEMORY-AS
foreign-address
c-type-internal
&OPTIONAL
offset
) value
)
This accessor is useful when operating with untyped
foreign pointers (FFI:FOREIGN-ADDRESS
) as opposed to typed ones
(represented by FFI:FOREIGN-VARIABLE
). It allows to type and
dereference the given pointer without the need to create an object of
type FFI:FOREIGN-VARIABLE
.
Alternatively, one could use (
(also FFI:FOREIGN-VALUE
(FFI:FOREIGN-VARIABLE
foreign-entity
c-type-internal
))SETF
able).
Note that c-type-internal
is the internal
representation of a foreign type, thus FFI:PARSE-C-TYPE
is required
with literal names or types, e.g. (
or FFI:MEMORY-AS
foreign-address
(FFI:PARSE-C-TYPE
'(FFI:C-ARRAY uint8 3)))(
.SETF
(FFI:MEMORY-AS
foreign-address
(FFI:PARSE-C-TYPE
'uint32)) 0)
Foreign C types are used in the “FFI”. They are not regular Common Lisp types or CLOS classes.
A c-type
is either a predefined C type or the name of a
type defined by FFI:DEF-C-TYPE
.
the predefined C types (c-type
)
simple-c-type
the simple C types
Lisp name | Lisp equivalent | C equivalent | ILU equivalent | Comment |
---|---|---|---|---|
NIL | NIL | void | as a result type only | |
BOOLEAN | BOOLEAN | int | BOOLEAN | |
CHARACTER | CHARACTER | char | SHORT CHARACTER | |
char | INTEGER | signed char | ||
uchar | INTEGER | unsigned char | ||
short | INTEGER | short | ||
ushort | INTEGER | unsigned short | ||
int | INTEGER | int | ||
uint | INTEGER | unsigned int | ||
long | INTEGER | long | ||
ulong | INTEGER | unsigned long | ||
uint8 | ( | uint8 | BYTE | |
sint8 | ( | sint8 | ||
uint16 | ( | uint16 | SHORT CARDINAL | |
sint16 | ( | sint16 | SHORT INTEGER | |
uint32 | ( | uint32 | CARDINAL | |
sint32 | ( | sint32 | INTEGER | |
uint64 | ( | uint64 | LONG CARDINAL | does not work on all platforms |
sint64 | ( | sint64 | LONG INTEGER | does not work on all platforms |
SINGLE-FLOAT | SINGLE-FLOAT | float | ||
DOUBLE-FLOAT | DOUBLE-FLOAT | double |
NIL
is accepted as a FFI:C-POINTER and
treated as NULL
; when a function wants to return a NULL
FFI:C-POINTER, it actually returns NIL
.
(FFI:C-POINTER
c-type
)
c-type
*: a pointer to a single item of the given
c-type
. It differs from (FFI:C-PTR-NULL
c-type
)
(see below) in that no conversion to and from
Lisp will occur (beyond the usual one of the C NULL
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.(FFI:C-STRUCT
class
(ident1
c-type1
) ...
(identn
c-typen
))
This type is equivalent to what C calls
struct { c-type1
ident1
; ...;
c-typen
identn
; }.
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
.
class
may also be a CONS
of a SYMBOL
(as above) and
a LIST
of FFI:DEF-C-STRUCT
options.
(FFI:C-UNION
(ident1
c-type1
) ...
(identn
c-typen
))
c-type1
ident1
; ...;
c-typen
identn
;
}.
Conversion to and from Lisp assumes that a value is to be viewed as
being of c-type1
.
(FFI:C-ARRAY
c-type
dim1
)
(FFI:C-ARRAY
c-type
(dim1
...
dimn
))
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.(FFI:C-ARRAY-MAX
c-type
maxdimension
)
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 superfluous elements.
(FFI:C-FUNCTION (:ARGUMENTS
{(argument
a-c-type
[PARAM-MODE
[ALLOCATION
]])}*)
(:RETURN-TYPE
r-c-type
[ALLOCATION
])
(:LANGUAGE
language
))
(r-c-type
(*)
(a-c-type1
, ...))
.
Conversion between C functions and Lisp functions
is transparent, and NULL
/NIL
is recognized and
accepted.(FFI:C-PTR
c-type
)
c-type
*: a pointer to a single item of the given
c-type
.(FFI:C-PTR-NULL c-type
)
c-type
*: a pointer to a single item of the given
c-type
, with the exception that C NULL
corresponds to
Lisp NIL
.(FFI:C-ARRAY-PTR c-type
)
c-type
(*)[]: a pointer to a zero-terminated array of
items of the given c-type
.The conversion of FFI:C-STRING,
(FFI:C-ARRAY
,
CHARACTER
dim1
)(FFI:C-ARRAY-MAX
,
CHARACTER
maxdimension
)(FFI:C-ARRAY-PTR
is governed by CHARACTER
)CUSTOM:*FOREIGN-ENCODING*
and dimensions are given
in bytes.
The conversion of CHARACTER
, and as such of
(FFI:C-PTR
, or
CHARACTER
)(FFI:C-PTR-NULL
, as well as
that of multi-dimensional arrays CHARACTER
)(FFI:C-ARRAY
,
are governed by CHARACTER
(dim1
... dimn
))CUSTOM:*FOREIGN-ENCODING*
if the latter is a 1:1 encoding, or by the
ASCII encoding otherwise.
Remember that the C type char is
a numeric type and does not use CHARACTER
EXT:ENCODING
s.
FFI:C-FUNCTION, FFI:DEF-CALL-IN
, FFI:DEF-CALL-OUT
take a :LANGUAGE
argument.
The language
is either :C
(denotes K&R C) or :STDC
(denotes ANSI C) or :STDC-STDCALL
(denotes ANSI C
with the “stdcall” calling convention).
It specifies whether the C function (caller or callee) has been
compiled by a K&R C compiler or by an ANSI C compiler,
and possibly the calling convention.
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.
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.
For a foreign variable x
the form (
is not necessarily true, since every time EQL
x
x
)x
is
evaluated its foreign value is converted to a fresh Lisp value.
Ergo, (
modifies this
fresh Lisp value (immediately discarded), not the foreign data.
Use SETF
(AREF
x
n
) y
)FFI:ELEMENT
et al instead, see Section 31.3.6, “Operations on foreign places”.
Foreign variables are defined using FFI:DEF-C-VAR
and FFI:WITH-C-VAR
.
A FFI:FOREIGN-VARIABLE
name
defined by FFI:DEF-C-VAR
, FFI:WITH-C-VAR
or FFI:WITH-C-PLACE
defines a place,
i.e., a form which can also be used as argument to SETF
.
(An “lvalue” in C terminology.)
The following operations are available on foreign places:
FFI:ELEMENT | FFI:C-VAR-ADDRESS |
FFI:DEREF | FFI:C-VAR-OBJECT |
FFI:SLOT | FFI:TYPEOF |
FFI:CAST | FFI:SIZEOF |
FFI:OFFSET | FFI:BITSIZEOF |
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.
A call-out function is a foreign function called from Lisp: control flow temporarily leaves Lisp. A call-in function is a Lisp function called from the foreign language: control flow temporary enters Lisp.
The following operators define foreign functions:
FFI:DEF-CALL-IN | FFI:FOREIGN-FUNCTION |
FFI:DEF-CALL-OUT |
When passed to and from functions, allocation of arguments and results is handled as follows:
Values of SIMPLE-C-TYPE
, FFI:C-POINTER are passed on the stack,
with dynamic extent. The ALLOCATION
is effectively ignored.
Values of type FFI:C-STRING, FFI:C-PTR, FFI:C-PTR-NULL, FFI:C-ARRAY-PTR
need storage. The ALLOCATION
specifies the allocation policy:
If no ALLOCATION
is specified, the default ALLOCATION
is
:NONE
for most types, but :ALLOCA
for FFI:C-STRING and FFI:C-PTR and
FFI:C-PTR-NULL and FFI:C-ARRAY-PTR and for :OUT
arguments.
The :MALLOC-FREE
policy provides the ability to pass
arbitrarily nested structures within a single conversion.
:MALLOC-FREE
malloc
and
never deallocates it. The C function is supposed to call
free
when done with it.:ALLOCA
:NONE
Lisp assumes that the pointer already points to a valid area of the proper size and puts the result value there.
This is dangerous and deprecated.
:MALLOC-FREE
free
on it when done.
:NONE
Passing FFI:C-STRUCT, FFI:C-UNION,
FFI:C-ARRAY, FFI:C-ARRAY-MAX values as arguments (not via pointers) is
only possible to the extent the C compiler supports it.
Most C compilers do it right, but some C compilers
(such as gcc on hppa,
x86_64 and Win32)
have problems with this.
The recommended workaround is to pass pointers; this is fully supported.
See also this <clisp-list@lists.sourceforge.net>
(http://lists.sourceforge.net/lists/listinfo/clisp-list)
message.
A function parameter's PARAM-MODE
may be
:IN
(means: read-only)::OUT
(means: write-only):ALLOCATION
= :ALLOCA
.:IN-OUT
(means: read-write)::OUT
value is returned as an additional multiple value.
The default is :IN
.
Example 31.1. Simple declarations and access
The C declaration
struct foo { int a; struct foo * b[100]; };
corresponds to
(def-c-struct foo (a int) (b (c-array (c-ptr foo) 100)))
The element access
struct foo f; f.b[7].a
corresponds to
(declare (type foo f)) (foo-a (aref (foo-b f) 7)) or (slot-value (aref (slot-value f 'b) 7) 'a)
Example 31.2. external C variable and some accesses
struct bar { short x, y; char a, b; int z; struct bar * n; }; extern struct bar * my_struct; my_struct->x++; my_struct->a = 5; my_struct = my_struct->n;
corresponds to
(def-c-struct bar (x short) (y short) (a char) (b char) ; or (b character) if it represents a character, not a number (z int) (n (c-ptr bar))) (def-c-var my_struct (:type (c-ptr bar))) (setq my_struct (let ((s my_struct)) (incf (slot-value s 'x)) s)) or (incf (slot my_struct 'x)) (setq my_struct (let ((s my_struct)) (setf (slot-value s 'a) 5) s)) or (setf (slot my_struct 'a) 5) (setq my_struct (slot-value my_struct 'n)) or (setq my_struct (deref (slot my_struct 'n)))
Example 31.3. Calling an external function
On ANSI C systems, <stdlib.h
>
contains the declarations:
typedef struct { int quot; /* Quotient */ int rem; /* Remainder */ } div_t; extern div_t div (int numer, int denom);
This translates to
(def-c-struct (div_t :typedef) (quot int) (rem int)) (default-foreign-language :stdc) (def-call-out div (:arguments (numer int) (denom int)) (:return-type div_t))
Sample call from within Lisp (after running clisp-link):
(div 20 3)
⇒ #S(DIV_T :QUOT 6 :REM 2)
Example 31.4. Another example for calling an external function
Suppose the following is defined in a file
cfun.c
:
struct cfunr { int x; char *s; }; struct cfunr * cfun (int i,char *s,struct cfunr * r,int a[10]) { int j; struct cfunr * r2; printf("i = %d\n", i); printf("s = %s\n", s); printf("r->x = %d\n", r->x); printf("r->s = %s\n", r->s); for (j = 0; j < 10; j++) printf("a[%d] = %d.\n", j, a[j]); r2 = (struct cfunr *) malloc (sizeof (struct cfunr)); r2->x = i+5; r2->s = "A C string"; return r2; }
It is possible to call this function from Lisp using the file
callcfun.lisp
(do not call it
cfun.lisp
- COMPILE-FILE
will
overwrite
cfun.c
) whose contents is:
(DEFPACKAGE
"TEST-C-CALL" (:use “COMMON-LISP” “FFI”)) (IN-PACKAGE
"TEST-C-CALL") (eval-when (compile) (setqFFI:*OUTPUT-C-FUNCTIONS*
t)) (def-c-struct cfunr (x int) (s c-string)) (default-foreign-language :stdc) (def-call-out cfun (:arguments (i int) (s c-string) (r (c-ptr cfunr) :in :alloca) (a (c-ptr (c-array int 10)) :in :alloca)) (:return-type (c-ptr cfunr))) (defun call-cfun () (cfun 5 "A Lisp string" (make-cfunr :x 10 :s "Another Lisp string") '#(0 1 2 3 4 5 6 7 8 9)))
Use the module facility:
$
clisp-link create-module-set cfun callcfun.c$
cc -O -c cfun.c$
cd cfun$
ln -s ../cfun.o cfun.o Add cfun.o to NEW_LIBS and NEW_FILES in link.sh.$
cd ..$
base/lisp.run -M base/lispinit.mem -c callcfun.lisp$
clisp-link add-module-set cfun base base+cfun$
base+cfun/lisp.run -M base+cfun/lispinit.mem -i callcfun > (test-c-call::call-cfun) i = 5 s = A Lisp string r->x = 10 r->s = Another Lisp string a[0] = 0. a[1] = 1. a[2] = 2. a[3] = 3. a[4] = 4. a[5] = 5. a[6] = 6. a[7] = 7. a[8] = 8. a[9] = 9. #S(TEST-C-CALL::CFUNR :X 10 :S "A C string") >$
rm -r base+cfun
Note that there is a memory leak here: The return value
r2
of cfun()
is
malloc
ed but never free
d. Specifying
(:return-type (c-ptr cfunr) :malloc-free)
is not an alternative because this would also
free(r2->x)
but r2->x
is a
pointer to static data.
The memory leak can be avoided using
(:return-type (c-pointer cfunr))
instead, in conjunction with
(defun call-cfun () (let ((data (cfun ...))) (prog1 (FFI:FOREIGN-VALUE
data) (FFI:FOREIGN-FREE
data :FULL nil))))
Example 31.5. Accessing cpp macros
Suppose you are interfacing to a library mylib.so
which defines macros and inline
functions
in mylib.h
:
#define FOO(x) ..... inline int bar (int x) { ... }
To make them available from CLISP, write these forms into the lisp
file my.lisp
:
(FFI:C-LINES
"#include <mylib.h> int my_foo (int x) { return FOO(x); } int my_bar (int x) { return bar(x); }~%") (FFI:DEF-CALL-OUT
my-foo (:name "my_foo") (:arguments (x int)) (:return-type int)) (FFI:DEF-CALL-OUT
my-bar (:name "my_bar") (:arguments (x int)) (:return-type int))
Compiling this file will produce my.c
and my.fas
and you have two options:
Compile my.c
into my.o
with
$
gcc -c my.c -lmylib
and use clisp-link to create a new CLISP linking set.
Add (:library "my.dll")
to the
FFI:DEF-CALL-OUT
forms, compile my.c
into my.so
(or my.dll
on
Win32) with
$
gcc -shared -o my.so my.c -lmylib
and load my.fas
.
Of course, you could have created my1.c
containing
#include <mylib.h> int my_foo (int x) { return FOO(x); } int my_bar (int x) { return bar(x); }
manually, but FFI:C-LINES
allows you to keep the
definitions of my_foo
and my-foo
close together for easier maintenance.
Example 31.6. Calling Lisp from C
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.
extern void lispsort_begin (int); void* lispsort_function; void lispsort_double (int n, double * array) { double * sorted_array; int i; lispsort_begin(n); /* store #'sort2 in lispsort_function */ sorted_array = ((double * (*) (double *)) lispsort_function) (array); for (i = 0; i < n; i++) array[i] = sorted_array[i]; free(sorted_array); }
This is accompanied by sort2.lisp
:
(DEFPACKAGE
"FFI-TEST" (:use “COMMON-LISP” “FFI”)) (IN-PACKAGE
"FFI-TEST") (eval-when (compile) (setqFFI:*OUTPUT-C-FUNCTIONS*
t)) (def-call-in lispsort_begin (:arguments (n int)) (:return-type nil) (:language :stdc)) (def-c-var lispsort_function (:type c-pointer)) (defun lispsort_begin (n) (setf (cast lispsort_function `(c-function (:arguments (v (c-ptr (c-array double-float ,n)))) (:return-type (c-ptr (c-array double-float ,n)) :malloc-free))) #'sort2)) (defun sort2 (v) (declare (type vector v)) (sort v #'<))
To test this, use the following test file sorttest.lisp
:
(eval-when (compile) (setq FFI:*OUTPUT-C-FUNCTIONS*
t))
(def-call-out sort10
(:name "lispsort_double")
(:language :stdc)
(:arguments (n int)
(array (c-ptr (c-array double-float 10)) :in-out)))
Now try
$
clisp-link create-module-set sort sort2.c sorttest.c$
cc -O -c sort1.c$
cd sort$
ln -s ../sort1.o sort1.o
Add sort1.o
to NEW_LIBS
and NEW_FILES
in link.sh.
Create a file package.lisp
containing the form
(MAKE-PACKAGE
"FFI-TEST" :use '(“COMMON-LISP” “FFI”))
and add package.lisp
to TO_PRELOAD
in link.sh.
Proceed:
$
cd ..$
base/lisp.run -M base/lispinit.mem -c sort2.lisp sorttest.lisp$
clisp-link add-module-set sort base base+sort$
base+sort/lisp.run -M base+sort/lispinit.mem -i sort2 sorttest > (sort10 10 '#(0.501d0 0.528d0 0.615d0 0.550d0 0.711d0 0.523d0 0.585d0 0.670d0 0.271d0 0.063d0)) #(0.063d0 0.271d0 0.501d0 0.523d0 0.528d0 0.55d0 0.585d0 0.615d0 0.67d0 0.711d0)$
rm -r base+sort
Example 31.7. Calling Lisp from C dynamically
Create a dynamic library lispdll
(#P".dll"
on Win32,
#P".so"
on UNIX)
with the following function:
typedef int (*LispFunc)(int parameter); int CallInFunc(LispFunc f) { return f(5)+11; }
and call it from Lisp:
(ffi:def-call-out callout (:name "CallInFunc") (:library "lispdll.dll") (:arguments (function-arg (ffi:c-function (:arguments (number ffi:int)) (:return-type ffi:int) (:language :stdc)))) (:return-type ffi:int) (:language :stdc)) (defun f (x) (* x 2)) ⇒F
(callout #'f) ⇒21
Example 31.8. Variable size arguments:
calling gethostname
from CLISP
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 namelen
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
namelen
characters, regardless of what the
poor char* C prototype says, to be used like a
C string (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.
(FFI:DEF-CALL-OUT
gethostname (:arguments (name (FFI:C-PTR (FFI:C-ARRAY-MAX ffi:char 256)):OUT
:ALLOCA
) (len ffi:int)) (:language :stdc) (:return-type ffi:int)) (defun myhostname () (multiple-value-bind (success name) ;;:OUT
and:IN-OUT
parameters are returned as multiple values (gethostname 256) (if (zerop success) name (error ...)))) ;;strerror
(errno
) (defvar hostname (myhostname))
Example 31.9. Accessing variables in shared libraries
Suppose one wants to access and modify variables that reside in shared libraries:
struct bar { double x, y; double out; }; struct bar my_struct = {10.0, 20.5, 0.0}; double test_dll(struct bar *ptr) { return ptr->out = ptr->out + ptr->x + ptr->y; }
This is compiled to libtest.so
(or
libtest.dll
, depending on your platform).
Use the following lisp code:
(USE-PACKAGE
“FFI”) (FFI:DEF-C-STRUCT
bar (x double-float) (y double-float) (out double-float)) (FFI:DEF-CALL-OUT
get-own-c-float (:library "libtest.so") (:language :stdc) (:name "test_dll") (:arguments (ptr c-pointer :in :alloca)) (:return-type double-float)) (FFI:DEF-C-VAR
my-c-var (:name "my_struct") (:library "libtest.so") (:type (c-ptr bar)))
Note that get-own-c-float
takes a
FFI:C-POINTER, not a (FFI:C-PTR bar)
as the
argument.
Now you can access call get-own-c-float
on
my-c-var
:
(FFI:C-VAR-ADDRESS
my-c-var) ⇒#<FOREIGN-ADDRESS #x282935D8>
(get-own-c-float (FFI:C-VAR-ADDRESS
my-c-var)) ⇒30.5d0
(get-own-c-float (FFI:C-VAR-ADDRESS
my-c-var)) ⇒61.0d0
(get-own-c-float (FFI:C-VAR-ADDRESS
my-c-var)) ⇒91.5d0
(get-own-c-float (FFI:C-VAR-ADDRESS
my-c-var)) ⇒122.0d0
Example 31.10. Controlling validity of resources
FFI:SET-FOREIGN-POINTER
is useful in conjunction with (
to limit the extent of external resources.
Closing twice can be avoided by checking SETF
FFI:VALIDP
)FFI:VALIDP
.
All pointers depending on this resource can be disabled at once upon
close by sharing their FFI:FOREIGN-POINTER
using FFI:SET-FOREIGN-POINTER
.
(def-c-type PGconn c-pointer) ; opaque pointer (def-call-out PQconnectdb (:return-type PGconn) (:arguments (conninfo c-string))) (defun sql-connect (conninfo) (let ((conn (PQconnectdb conninfo))) (unless conn (error "NULL pointer")) ;; may wish to useEXT:FINALIZE
as well (FFI:SET-FOREIGN-POINTER
conn:COPY
))) (defun sql-dependent-resource (conn arg1) (let ((res (PQxxx conn arg1))) (FFI:SET-FOREIGN-POINTER
res conn))) (defun sql-close (connection) (when (FFI:VALIDP
connection) (PQfinish connection) (setf (FFI:VALIDP
connection) nil) T))
Sharing FFI:FOREIGN-POINTER
goes both ways: invalidating
the dependent resource will invalidate the primary one.
An alternative approach to resource management, more suitable to non-“FFI” modules, is implemented in the berkeley-db module, see Section 32.4.2, “Closing handles”.
Example 31.11. Float point array computations
Save this code into sum.c
:
double sum (int len, double *vec) { int i; double s=0; for (i=0; i<len; i++) s+= vec[i]; return s; }
and compile it with
$
gcc -shared -o libsum.so sum.c
Now you can sum doubles:
(FFI:DEF-CALL-OUT
sum (:name "sum") (:library "libsum.so") (:language :stdc) (:return-type double-float) (:arguments (len int) (vec (FFI:C-ARRAY-PTR double-float)))) (sum 3 #(1d0 2d0 3d0)) ⇒6d0
You can find more information and examples of the CLISP
“FFI” in the following <clisp-list@lists.sourceforge.net>
(http://lists.sourceforge.net/lists/listinfo/clisp-list) messages:
Even more examples can be found in the file
tests/ffi.tst
in the CLISP source distribution.
These notes document CLISP version 2.41 | Last modified: 2006-10-13 |