This library provides an interface to C++ from lisp. It was inspired by Julia's libcxxwrap.
- CLCXX is installed
Clone into home/common-lisp directory. Then asdf:test-system "cxx"
- Fundamental types
- Pointers
- Classes can add public members and functions
- POD
- functions, std::function and lambda
C++ type | Lisp cffi type | Allocation/copy |
---|---|---|
fundamental | same | copy value |
POD struct | same | copy value |
std::string | :string | allocate c string |
const char* | :string | allocate c string |
reference | :pointer | copy pointer address |
class | :pointer | allocate class object |
for allocated memory a finalizer is defined for these objects.
First you will need to create a C++ glue library that CFF will use when calling C++ functions from Lisp. Compile the code below as a shared library say, libtest.so or libtest.dylib. The commands to do this will vary by compiler and operating system. For testing, it is always good to put the library in the same directory as the lisp code, and ensure that you set the current working directory in your Lisp image. CFFI can take some fiddling to find all the dependencies. This is not unique to this project, all Lisp C/C++ bindings with CFFI have the same requirements.
#include <string>
#include "clcxx/clcxx.hpp"
std::string greet() { return "Hello, World"; }
int Int(int x) { return x + 100; }
float Float(float y) { return y + 100.34; }
auto gr(std::complex<float> x) { return x; }
std::string hi(char* s) { return std::string("hi, " + std::string(s)); }
void ref_int(int& x) { x += 30; }
void ref_class(xx& x) { x.y = 1000000; }
class xx {
public:
xx(int xx, int yy) : y(yy), x(xx) {}
std::string greet() { return "Hello, World"; }
int y;
int x;
};
CLCXX_PACKAGE TEST(clcxx::Package& pack) {
pack.defun("hi", &hi);
pack.defun("test-int", F_PTR(&Int));
pack.defun("greet", F_PTR(&greet));
pack.defun("test-float", F_PTR(&Float));
pack.defun("test-complex", F_PTR(&gr));
pack.defun("ref-int", F_PTR(&ref_int));
pack.defun("ref-class", F_PTR(&ref_class));
pack.defclass<xx, false>("xx")
.member("y", &xx::y)
.defmethod("foo", F_PTR(&xx::greet))
.defmethod("foo.x", F_PTR([](xx x){return x.x;}))
.constructor<int, int>(); // could be .constructor<int, int>("make-my-xx-class");
}
You should walk through the commands below in the REPL to get an idea of how the bindings work. Before you start, ensure you have the dependencies loaded:
(ql:quickload "cffi")
(ql:quickload "cxx")
(defpackage cxx/test
(:use :cl))
(in-package :cxx/test)
;;; Change the pathname to match that of your system.
(pushnew (merge-pathnames #p"ros/lisp-demo/lib/" (user-homedir-pathname))
cffi:*foreign-library-directories*
:test #'equal)
(cffi:define-foreign-library my-lib
(t (:default "libtest")))
(cffi:use-foreign-library my-lib)
(cxx:init)
(cxx:add-package "TEST" "TEST")
(test:greet)
(test:hi "Cxx")
To create a C++ class you should have the following:
- C++
class A {
public:
A(int A, int yy) : y(yy), x(A) {}
int y;
int x;
};
CLCXX_PACKAGE class(clcxx::Package& pack) {
pack.defclass<A, false>("A")
.constructor<int, int>("create-my-A")
.member("y", &A::y)
.member("x", &A::x);
}
- lisp
(defvar my-a-class (class:create-my-A 10 20))
See test files or the cl-cxx-eigen project for further examples.
- CLCXX is used to expose
C++
function/lambda/member_function/overloaded_function toC
function pointer that lisp cffi would call.C++
code is compiled into a shared library --for internals see CLCXX--.
The following steps are expected by this package:
- load
C++
shared library(cffi:use-foreign-library my-lib)
. - call
(cxx:init)
: this function callsclcxx_init(void (*error_handler)(char *), void (*reg_data_callback)(clcxx::MetaData *, uint8_t))
.error_handler
is used to makeC++
call lisperror
function.reg_data_callback
is used to passC
function pointer, function paramters types and return type to lisp for registering lispcffi
function.
- call
(cxx:add-package "TEST" "TEST")
: this function callsregister_package(const char *cl_pack,void (*regfunc)(clcxx::Package &))
.cl_pack
is the lisp package name.regfunc
is the function name in the compiledC++
shared libraryCLCXX_PACKAGE regfunc(clcxx::Package& pack)
.
C++
package functions are avialable.
Tested on:
- SBCL 1.4.5 on debian
- CCL 1.12 on MacOS 10.13.6
- test functions
- code refactor
- global objects sometimes don't call its finalizers!
- classes
- references
- Smart pointers
- Tuple
- benchmark
- CL-CXX-JIT is a new project to make the process easier example:
(from '("<cmath>") 'import '("static_cast<double(*)(double)>(std::sin)" . "cpp-sin")),
then can be called with(cpp-sin 0d0)
. CL-CXX-JIT package recompiles c++ files every time, so can be used for quick experimentations, then usecl-cxx
.
Copyright (c) 2018 Islam Omar ([email protected])
Licensed under the MIT License.