Previous: The C language interface, Up: The compiler


5.8 The old C language interface

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.

— Macro: defentry {function parameter-list C-function}

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 symbol void 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 following defentry form defines a Lisp function tak from which the C function tak 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 ().

— Macro: defla {name lambda-list {declaration | doc-string}*}

When interpreted, defla is exactly the same as defun. 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 for defla forms. The primary use of defla 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.

— Function: defCbody name args-types result-type C-expr

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)")
— Function: definline {name args-types result-type C-expr}

definline behaves exactly as defCbody. Moreover, after a definline 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 @}