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.
— Macro:Clines{{string}*}When the ECL compiler encounters a macro form
(Clines, it simply outputs thestring1 ... stringn)stringsinto the c-file. The arguments are not evaluated and each argument must be a string. Eachstringmay consist of any number of lines, and separate lines in thestringare placed in separate lines in the c-file. In addition, eachstringopens a fresh line in the c-file, i.e., the first character in thestringis 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 thestringor as the first character of a line within thestring.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.
— Macro:c-inline{args-listarg-C-typesoutput-C-typeC-expr&key(side-effects) (Tone-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 inargs-listand produces a single value. Behind the curtains, the arguments ofargs-list(which can be any valid Lisp form) are coerced to the the C types given inarg-C-types, passed to the C expressionC-expr, and coerced back to Lisp using the C typeoutput-C-typeas a guide. Multiple return values can be returned by settingoutput-C-typeto(values type-1 type-2 ...).
C-expris 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-lineris true), then the whole expression is interpreted as the output value. But if the code, on the other hand, is a multiline expression (one-lineris 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))