I needed a CL interpreter which would host the compiler (which I wrote in Common Lisp) which calls the LLVM-IR library to generate llvm::Module objects which contains llvm::Function objects that are Just-In-Time compiled into machine code and called via C function pointers from the Common Lisp or C++ code.
I did not want to write a Common Lisp compiler in C++ (shudder).
This project started as an archaic Sexp walking Lisp interpreter that grew into a very slow Common Lisp Sexp walking interpreter within which I wrote a reasonably fast self-hosting Common Lisp compiler that generates LLVM-IR.
I didn't know that when I started. I know that now - the bridge doesn't require the compiler and LLVM backend
- the CL/C++ bridge is all C++ template programming, lots and lots of template programming.
It's styled after the boost::python library if you are familiar with it.
Once I started writing the compiler and exposing and using the LLVM backend I just kept going.
Although, having intimate familiarity with the Common Lisp implementation I wrote has greatly facilitated the development of the bridge.
I did not state that ECL cannot interface with C++ - I know better than that.
On the other hand, I need it to be very easy to interface CL to C++ because I have a lot of C++ and nothing I saw in the CL world seemed to do the job.
Now if I want to interface a C++ function foo(int x, double y) to CL I add the following code to my C++ code.
Or the macro:
That's it for simple cases. If the function needs lambda-list processing then it is:
af_def("CORE","foo",&foo,"(x y &optional (z 999))");
That specifies the package, the function-name in CL, the pointer to the function and the lambda-list as a string which is parsed and associated with the wrapper for the function.
If I want to expose the C++ class Foo with method Foo::bar (default lambda-list processing) and Foo::baz (lambda-list processing) I currently copy-and-paste-and-edit a wrapper class Foo_O into a header file (I should be able to eliminate the need for this with more template programming) and I add the following to my C++ code.
.def("baz",&Foo::baz,"(a b &key (gamma (+ a b) gamma-p))")
That's it for this simple example. Different function arity, lambda-list processing, argument type-conversion, result-conversion, memory-management, multiple and single inheritance, virtual-function handling is all done automatically with C++ template programming.
We could get it to work with ECL as well if you want it - although it's going to take some work.
Then we could incorporate the LLVM-IR generating compiler into ECL.
SWIG ran out of steam for me pretty early on.
I've had to develop solutions to all of these problems - so I'm familiar with them.
1) C++ classes behave like CL structures.
2) C++ functions calling CL and vice versa is taken care of.
3) Memory management is currently handled using reference counted C++ shared_ptr/weak_ptr. I plan to add mark-and-sweep GC later.
4) Virtual functions, regular functions and overloaded functions are all handles. When you have overloaded functions with different types you have to disambiguate them by getting a pointer to each function. There is no way to avoid this.