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
(Clinesstring1 ... 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#defineand#includewill 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
Clinesmacro 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-inlineis 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-inlineform 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))