Next: , Previous: Inspecting generated C code, Up: The compiler


5.6 Embedding C code in lisp source

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 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.

— Macro: c-inline {args-list arg-C-types output-C-type C-expr &key (side-effects 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 the c-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))