ECL Exploration: Continued
Exploring the embedding and interop features of ECL. Continued.
[ Check out all posts in “lisp” series here. ]
In my previous post, I started exploring Embeddable Common-Lisp. I was looking into this example from the ECL repo as a starting point. I will share some of my learning so far.
The initial integration into a C/C++ library is pretty straightforward:1
- There are various ways to build the lisp code, but I focused on the
asdf
based method. You just add a build rule that’s a few lines long. It triggersecl
to build a (static or shared) library from an ASDF based system you point at. - As part of the build rule, you also declare an “entry point” to generate. This entry point will be used to initialize the library later.
- On the C/C++ side, you boot ecl, and call the library init function you just created.
- You are now ready to interact with the system.
- While building your C/C++ code, you link against the library you created earlier.
The API that ecl exports is pretty rich. It has:
- C versions of many CL functions, which are
cl_
prefixed. - Functions for interop-related functionality, convenience etc. These are
ecl_
prefixed.
The naming conventions page in docs have the details.
CL Object
One interesting concept is the cl_object
, which is the box for CL values. The definition:
typedef union cl_lispunion *cl_object;
Internals are detailed here. The type cl_lispunion
is essentially a tagged union
. But I am planning to start a series on methods used for boxing values in dynamic languages, anyway. So I will move on for now.
The cl_object
is the input and the return type of most C side APIs. Obviously, the requirement to explicitly generate cl_object
s for values makes it a slightly verbose to use the APIs from C side, but that is expected.
I am currently trying to have a better grasp of the memory management aspect.
Defun Preprocessor
While navigating the ECL codebase, I noticed that the src/c
directory is full of files with .d
extension. It took me a while to figure out that these source files are intended to be processed by the tool called “defun preprocessor” (dpp
), which generates the corresponding C sources.
So those files are mostly normal C, with some parts that use special syntax to be expanded by this preprocessor, to C. Here is an example line from “src/c/file.d”:
if (b == @':eof') b = ECL_NIL;
Above expands to a C form:
if (b == ECL_SYM(":EOF",1256)) b = ECL_NIL;
And ECL_SYM
macro just indexes an array called cl_symbols
using the index (second argument) arg.
Another example: this defun
declaration expands to a function with the prototype below:
cl_object cl_read(cl_narg narg, ...)
Cool!
Thanks for reading! If you find technical errors, please report in the blog’s Issues page.
-
At least for the method I focused on. ↩