Next: The C language interface, Previous: Inspecting generated C code, Up: The compiler
There are several mechanism to integrate C code within ECL, but everything is built around two functions that allow the user to embed arbitrary C/C++ code into Lisp source code.
The two mechanisms are the Clines
and the c-inline
special
forms. The first one permits to insert code in the intermediate C/C++ file
generated by the ECL compiler. Such a form outputs no value and takes
no arguments, except a series of strings which are inserted literally,
such as #include
or #define
statements, function definitions, etc.
When the ECL compiler encounters a macro form
(Clines
string1 ... stringn)
, it simply outputs the strings into the c-file. The arguments are not evaluated and each argument must be a string. Each string may consist of any number of lines, and separate lines in the string are placed in separate lines in the c-file. In addition, each string opens a fresh line in the c-file, i.e., the first character in the string is placed at the first column of a line. Therefore, C-language preprocessor commands such as#define
and#include
will be recognized as such by the C compiler, if the ' # ' sign appears as the first character of the string or as the first character of a line within the string.When interpreted, a
Clines
macro form expands to ().
(use-package "FFI") (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))); " " } " ) (defun tak (x y z) (c-inline (x y z) (:int :int :int) :int "tak(#0,#1,#2)" :one-liner t))
The second mechanism, which you already appreciate in the example above, is the
c-inline
special form. This powerful method allows the user to insert C
code which is evaluated, and which can accept values and return values from and
to the Lisp world, with an automatic convertion taking place in both directions.
T
) (one-liner T
)}
c-inline
is a special form that can only be used in compiled code. For all purposes it behaves as a Lisp form, which takes the arguments given in args-list and produces a single value. Behind the curtains, the arguments of args-list (which can be any valid Lisp form) are coerced to the the C types given in arg-C-types, passed to the C expression C-expr, and coerced back to Lisp using the C type output-C-type as a guide. Multiple return values can be returned by setting output-C-type to(values type-1 type-2 ...)
.C-expr is a string containing C code and maybe some special escape codes. First, the arguments of the form may be retrieved as
#0
,#1
, etc. Second, if thec-inline
form is a one-line C expression (That is, one-liner is true), then the whole expression is interpreted as the output value. But if the code, on the other hand, is a multiline expression (one-liner is false), the form has to be output using@(return) =...
. Multiple values are returned as@(return 0)=... ; @(return 1)=...;
. Finally, Lisp constants may be used in the C code making use of the prefix@
.(use-package "FFI") (Clines " #include <math.h> double foo (double x, double y) { return sinh(x) * y; }") (defvar *a* (c-inline (1.23) (:double) :double "foo(#0,1.44)" :side-effects nil :one-liner t)) (defvar *b* (c-inline (1.23) (:double) :double "{cl_object x = symbol_value(@*a*); @(return) = foo(#0,object_to_float(x));}" :side-effects nil :one-liner nil))