Previous: The C language interface, Up: The compiler
In this section we list several macros and toplevel forms which are provided either for convenience or for compatibility with older versions of ECL. You should avoid using them when the UFFI-compatible interface provides similar functionality.
We define some terminology here which is used throughout this Chapter. A
C-id is either a Lisp string consisting of a valid C-language
identifier, or a Lisp symbol whose print-name, with all its alphabetic
characters turned into lower case, is a valid C identifier. Thus the symbol
foo
is equivalent to the string "foo"
when used as a C-id.
Similarly, a C-expr is a string that may be regarded as a
C-language expression. A C-type is one of the Lisp symbols
:int, :char, :float, :double,...
and :object
.
Each corresponds to a data type in the C language; :object
is
the type of Lisp object and other C-types are primitive data types in C.
defentry
defines a Lisp function whose body consists of the calling sequence to a C-language function. function is the name of the Lisp function to be defined, and C-function specifies the C function to be invoked. C-function must be either a list(
type C-id)
or C-id, where type and C-id are the type and the name of the C function. type must be a C-type or the symbolvoid
which means that the C function returns no value.(object
C-id)
may be abbreviated as C-id. parameter-list is a list of C-types for the parameters of the C function. For example, the followingdefentry
form defines a Lisp functiontak
from which the C functiontak
above is called.
(defentry tak (:int :int :int) (:int tak))
The Lisp function tak
defined by this defentry
form requires
three arguments. The arguments are converted to int
values before they
are passed to the C function. On return from the C function, the returned
int
value is converted to a Lisp integer (actually a fixnum) and this
fixnum will be returned as the value of the Lisp function. See below for type
conversion between Lisp and the C language.
A defentry
form is treated in the above way only when it appears as a
top-level form of a Lisp source file. Otherwise, a defentry
form
expands to ().
When interpreted,
defla
is exactly the same asdefun
. That is,(defla
name lambda-list . body)
expands to(defun
name lambda-list . body)
. However,defla
forms are completely ignored by the compiler; no C-language code will be generated fordefla
forms. The primary use ofdefla
is to define a Lisp function in two ways within a single Lisp source file; one in the C language and the other in Lisp.defla
is short for DEFine Lisp Alternative.
Suppose you have a Lisp source file whose contents are:
(use-package "FFI") ;;; C version of TAK. (Clines " int tak(x, y, z) int x, y, z; { if (y >= x) return(z); else return(tak(tak(x-1, y, z), tak(y-1, z, x), tak(z-1, x, y))); } " ) ;;; TAK calls the C function tak defined above. (defentry tak (:int :int :int) (:int tak)) ;;; The alternative Lisp definition of TAK. (defla tak (x y z) (if (>= y x) z (tak (tak (1- x) y z) (tak (1- y) z x) (tak (1- z) x y))))
When this file is loaded into ECL, the interpreter uses the Lisp version of
the tak
definition. Once this file has been compiled, and when the
generated fasl file is loaded into ECL, a function call to tak
is
actually the call to the C version of tak
.
The ECL compiler produces a function named name with as many arguments as arg-types. The C-expr is an arbitrary C expression where the arguments to the function are denoted by
#
i, where i is the integer corresponding to the argument position. The args-types is the list of Common-Lisp types of the arguments to the function, while result-type is the Common-Lisp type of the result. The actual arguments are coerced to the required types before executing the C-expr and the result is converted into a Lisp object.defCbody
is ignored by the interpreter.
For example, the logical AND of two integers could be defined as:
(defCbody logand (fixnum fixnum) fixnum "(#0) & (#1)")
definline
behaves exactly asdefCbody
. Moreover, after adefinline
definition has been supplied, the ECL compiler will expand inline any call to function name into code corresponding to the C language expression C-expr, provided that the actual arguments are of the specified type. If the actual arguments cannot be coerced to those types, the inline expansion is not performed.definline
is ignored by the interpreter.
For example, a function to access the n-th byte of a string and return it as an integer can be defined as follows:
(definline aref-byte (string fixnum) fixnum "(#0)->ust.ust_self[#1]")
The definitions of the C data structures used to represent \clisp objects can
be found in file ecl.h
in the directory "src/h"
of the source
distribution.
ECL converts a Lisp object into a C-language data by using the Common-Lisp
function coerce
: For the C-type int
(or char
), the object
is first coerced to a Lisp integer and the least significant 32-bit (or 8-bit)
field is used as the C int
(or char
). For the C-type
float
(or double
), the object is coerced to a single-float (or a
double-float) and this value is used as the C float
(or double
).
Conversion from a C data into a Lisp object is obvious: C char, int,
float,
and double
become the equivalent Lisp character
,
fixnum
, single-float
, and double-float
, respectively.
Here we list the complete syntax of Clines
, defentry
,
definline
and defCbody
macro forms.
Clines-form: (Clines @{string@}*) defentry-form: (defentry symbol (@{C-type@}*) @{C-function-name | (@{C-type | void@} C-function-name)@}) defCbody-form: (defCbody symbol (@{type@}*) type C-expr) definline-form: (defCbody symbol (@{type@}*) type C-expr) C-function-name: @{ string | symbol @} C-expr: string C-type: @{ object | int | char | float | double @}