Skip to content

Islam0mar/CL-CXX-JIT

Repository files navigation

CL-CXX-JIT - Common Lisp C++ JIT for exposing C++ functions

This library provides an interface to C++ from lisp. It compiles C++ code, then loads it into lisp. It was inspired by RCRL and the older project CL-CXX.

API

(from '("list of string headers") 'import "normal string is inserted as it is" '("function/function-pointer/mem-fun-pointer/lambda" . "name-used-in-lisp"))

Examples

SDL2 Example

sdl2.gif

(ql:quickload :cxx-jit)
(setf cxx-jit:*cxx-compiler-link-libs* "-lGL -lSDL2 -lSDL2main")

(cxx-jit:from '(
                "<SDL2/SDL.h>")
              'import
              '("[](){return SDL_Init(SDL_Init(SDL_INIT_VIDEO));}" . "init")
              '("SDL_CreateWindow" . "create-window")
              '("SDL_CreateRenderer" . "create-renderer")
              '("SDL_SetRenderDrawColor" . "set-color")
              '("SDL_DestroyWindow" . "destroy-window")
              '("SDL_RenderClear" . "clear-renderer")
              '("SDL_RenderPresent" . "renderer-render")
              '("SDL_Quit" . "sdl-quit"))

(init)
(setf wind (create-window "create-window" 0 0 600 700 0))
(setf rend (create-renderer wind -1 0))
(loop for x to (* 255 3)
      for r = (if (> x 255) 255 x)
      for g = (if (> x 255) (if (> x (* 2 255)) 255 (rem x 256)) 0)
      for b = (if (> x (* 2 255)) (rem x 256) 0)
      do
         (print x)
         (set-color rend r g b 255)
         (clear-renderer rend)
         (renderer-render rend)
         (sleep 0.01))

(destroy-window wind)
(sdl-quit)

Basic Example

Start with (ql:quickload :cxx-jit) (in-package cxx-jit)

(from '("<string>") 'import '("[](std::string x){return \"Hi, \"+x;}" . "hi"))
(hi "there!")

(from '("<cmath>") 'import '("static_cast<double(*)(double)>(std::sin)" . "cpp-sin"))
(cpp-sin 0d0)
(cpp-sin pi)
(cpp-sin (/ pi 2))

(from nil 'import "struct C{ auto hi(){return \"Hello, World\\n\";} auto bye(){return \"Bye\";} };" '("&C::bye" . "bye") '("&C::hi" . "hi") '("[](){static C x; return x;}" . "cc"))
(cc)
(hi *)
(bye **)
  ;;; structure  definition could be written in a header file then be used as the following:
(from '("c.hpp") 'import '("&C::bye" . "bye") '("&C::hi" . "hi") '("[](){static C x; return x;}" . "cc"))

Eigen Library Example

Start with (ql:quickload :cxx-jit) (in-package cxx-jit)

(from '("<Eigen/Core>" "<Eigen/Core>") 'import '("[](Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<double>,Eigen::Matrix<double, 3, 3>> x){
  std::stringstream s;
  s << x;
  return s.str();}" . "print-matrix"))

(from '("<Eigen/Core>" "<Eigen/Core>") 'import '("static_cast<const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<double>,Eigen::Matrix<double, 3, 3>> (*)()> (&Eigen::Matrix3d::Identity)" . "identity-matrix"))

(print-matrix (identity-matrix))

Prerequisites

  • common lisp supporting CFFI
  • working C++17 compiler
  • the following variables should be set according to OS and compiler used
variabledefault value
*cxx-compiler-executable-path*/usr/bin/g++
*cxx-compiler-flags*-std=c++17 -Wall -Wextra -I/usr/include/eigen3
*cxx-compiler-working-directory*/tmp/” #\/ ‘/’ should be the last character
+cxx-compiler-lib-name+(intern ”plugin”)
+cxx-compiler-wrap-cxx-path+shouldn’t be changed ”path to wrap-cxx.cpp
*cxx-compiler-internal-flags*-shared -fPIC -Wl,--no-undefined -Wl,--no-allow-shlib-undefined
for g++ and for clang++ ”-shared -fPIC -Wl,-undefined,error -Wl,-flat_namespace
*cxx-compiler-link-libs*-lm” these flags are added after ”-o output” to link correctly
*cxx-type-name-to-cffi-type-symbol-alist*alist to map c++ type name to cffi type example:”(push (cons "long unsigned int" :ulong) *)

Installation

Clone into home/common-lisp directory. Then (ql:quickload :cxx-jit-test)

Supported Types

C++ typeLisp cffi type
fundamentalsame
string:string
class:pointer
std::is_function:pointer
othernot implemented!

Under The Hood

  • function/lambda/member_function/function_pointer is wrapped into a dummy lambda class to have a unique template specialization.
    Import([&]() { return __VA_ARGS__; });
        
  • Import function calls DecayThenResolve with function pointer as the template specialization so thunk pointer is omitted and we only return the direct function pointer which will be used from lisp side.
  • InvocableTypeName returns a vector contains: [return type, class type for class function member, args]. It resolves C++ types as follows:
    • Fundamental types and pointers are passed directly
    • String is converted to char* with new[] operator, should be cleared with ClCxxDeleteObject(ptr, true)
    • Class/std::is_function is converted to void* with new[] operator, should be cleared with ClCxxDeleteObject(ptr, false)
    • rest report an issue for other cases
  • Meta data for each function defined is passed through a lisp callback with this data:
    typedef struct {
      // could be void*
      void (*thunk_ptr)();
      bool method_p;
      const char **type;  // memory handled in C++
      std::uint8_t type_size;
    } MetaData;
        

NOTE

Tested on:

  • SBCL 2.0.1 on debian

Todo List

Add redirect stdout : freopen("/tmp/tmp.txt", "w", stdout); @apemangr

Use trivial-garbage with ClCxxDeleteObject

Add non-polling from

Test functions

Benchmark

Better class interface

Copyright

Copyright (c) 2021 Islam Omar ([email protected])

License

Licensed under the MIT License.