From f1718395d52a03946a52f2e3d200b5fdbfb94f9e Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 18:38:21 +0100 Subject: [PATCH 01/46] added README.org in doc/ --- doc/README.org | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 doc/README.org diff --git a/doc/README.org b/doc/README.org new file mode 100644 index 0000000..ed460c2 --- /dev/null +++ b/doc/README.org @@ -0,0 +1,7 @@ +* Motivation + + This library provides an interface to C++ from lisp. It was inspired + by Julia's libcxxwrap and provides a similar functionality as + [pybind11](https://github.com/pybind/pybind11). + +* From ba335471f3dfd04be3476bee890574ffcf46dd2b Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 18:39:31 +0100 Subject: [PATCH 02/46] misc doc --- doc/README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index ed460c2..f2f28bf 100644 --- a/doc/README.org +++ b/doc/README.org @@ -2,6 +2,6 @@ This library provides an interface to C++ from lisp. It was inspired by Julia's libcxxwrap and provides a similar functionality as - [pybind11](https://github.com/pybind/pybind11). + [[https://github.com/pybind/pybind11][pybind11]]. * From 973987f3c9ce0e12106b95af52801797c580c77d Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 19:49:48 +0100 Subject: [PATCH 03/46] added block-diagram.svg --- doc/README.org | 51 +++++- doc/block-diagram.svg | 400 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 446 insertions(+), 5 deletions(-) create mode 100644 doc/block-diagram.svg diff --git a/doc/README.org b/doc/README.org index f2f28bf..ac57c70 100644 --- a/doc/README.org +++ b/doc/README.org @@ -1,7 +1,48 @@ -* Motivation +* Introduction - This library provides an interface to C++ from lisp. It was inspired - by Julia's libcxxwrap and provides a similar functionality as - [[https://github.com/pybind/pybind11][pybind11]]. + This library provides an interface to C++ from Common Lisp. It was + inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar functionality as + python's [[https://github.com/pybind/pybind11][pybind11]] for Common Lisp. -* + As [[https://cffi.common-lisp.dev/][CFFI]] is normally used for C code, there are no builtin functions + to deal with C++ extensions from Common Lisp like class definitions, + their instantiation, accessing and defining member functions, + etc. [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] is an attempt to step in and facilitate easy interfacing + from Common Lisp to C++ libs. + +* Architecture + + [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] consists of three parts: + + 1. [[https://github.com/Islam0mar/CLCXX][CLCXX]] + + A C​++ library which gets compiled once and installed systemwide, + visible by the C++ compiler. + + + 2. A custom C​++ library written by the end user which contains the + API bindings to the targeted C​++ library to be exposed to lisp. + + 3. [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] + + The Common Lisp part, which translates the definitions in the + custom C​++ library to CFFI code suitable to be used directly from + Common Lisp. + + The following diagram shows the relation of the different parts of + cl-cxx. + + [[./block-diagram.svg]] + + + Note that with this design, the definition of functions, structs, + etc. is *not* done directly on the Lisp side, as it's normally done + when directly working with standard CFFI, but rather on the C​++ + side, with the translations, name mangling, etc. done automatically + by cl-cxx. + + +Architecture - Block diagram, sequence diagrams for the calls from Lisp to C++ +Examples - 2 or 3 end to end examples, with extensive documentation +Future Direction - What is planned next, if anything. E.g. Auto-wrap style wrapping so you can point to a header. Roadmap for the C++ features that are missing. +Help Wanted - what can contributors help with, by skill level. diff --git a/doc/block-diagram.svg b/doc/block-diagram.svg new file mode 100644 index 0000000..54bc02b --- /dev/null +++ b/doc/block-diagram.svg @@ -0,0 +1,400 @@ + + + + + + + + + + user defined + provided + + + + + targeted C++ lib + + libClCxx + + C++ + Common Lisp + + + include <targeted C++ lib.h>include <ClCxx.h>Class Definitions, etc... of targeted C++ lib,their Common Lisp names,the "CUSTOM_PACKAGE" name,... + custom C++ lib + + + + (asdf:load :cffi)(asdf:load :cxx)(use-package :cffi)(use-package :cxx)(cffi:define-foreign-library clcxx (t (:default "libClCxx")))(cffi:define-foreign-library my-custom-lib (t (:default "customC++Lib")))(cffi:use-foreign-library clcxx)(cffi:use-foreign-library my-custom-lib)(cxx:init)(cxx:add-package "CUSTOM-PACKAGE" "CUSTOM_PACKAGE") + custom cl code + + + cl-cxx + + cffi + + + From 285051c1085ca8ced1244826b2107444538e114f Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 19:51:12 +0100 Subject: [PATCH 04/46] edits in README --- doc/README.org | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index ac57c70..513203d 100644 --- a/doc/README.org +++ b/doc/README.org @@ -20,7 +20,9 @@ visible by the C++ compiler. - 2. A custom C​++ library written by the end user which contains the + 2. A custom C​++ library + + Written by the end user which contains the API bindings to the targeted C​++ library to be exposed to lisp. 3. [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] From cfeaf0123f3a20e81f6378fa5f2a5197accbfae0 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 19:52:02 +0100 Subject: [PATCH 05/46] misc in README --- doc/README.org | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/README.org b/doc/README.org index 513203d..460f8f5 100644 --- a/doc/README.org +++ b/doc/README.org @@ -14,18 +14,18 @@ [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] consists of three parts: - 1. [[https://github.com/Islam0mar/CLCXX][CLCXX]] + - [[https://github.com/Islam0mar/CLCXX][CLCXX]] A C​++ library which gets compiled once and installed systemwide, visible by the C++ compiler. - 2. A custom C​++ library + - A custom C​++ library Written by the end user which contains the API bindings to the targeted C​++ library to be exposed to lisp. - 3. [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] + - [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] The Common Lisp part, which translates the definitions in the custom C​++ library to CFFI code suitable to be used directly from From bf40b393b4ade0d079be78c5872ecd39375f2545 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 19:59:28 +0100 Subject: [PATCH 06/46] misc. README --- doc/README.org | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/doc/README.org b/doc/README.org index 460f8f5..1a5ebbb 100644 --- a/doc/README.org +++ b/doc/README.org @@ -12,28 +12,41 @@ * Architecture - [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] consists of three parts: +** Provided parts + + The provided architecture consists of two parts: - [[https://github.com/Islam0mar/CLCXX][CLCXX]] A C​++ library which gets compiled once and installed systemwide, visible by the C++ compiler. - - - A custom C​++ library - - Written by the end user which contains the - API bindings to the targeted C​++ library to be exposed to lisp. - - [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] The Common Lisp part, which translates the definitions in the custom C​++ library to CFFI code suitable to be used directly from Common Lisp. - The following diagram shows the relation of the different parts of - cl-cxx. + In addition the targeted C++ Library plus its header files will also + be needed. + +** Custom parts + + There are also two user-defined parts: + + - A custom C​++ library + + This contains the API bindings to the targeted C​++ library to be + exposed to Lisp, package name definitions for the Lisp side, the + Lisp names of functions, methods, member functions, etc... + + - The custom Common Lisp code + + This contains the code to load the foreign libraries and + initialize the cxx system and package definitions from the C++ lib + to be accessible by Lisp. + The following diagram shows the relation of the different parts: [[./block-diagram.svg]] From 9750d656745432fcfbe4effd3fc830f5b11850d4 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:06:23 +0100 Subject: [PATCH 07/46] misc README --- doc/README.org | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/doc/README.org b/doc/README.org index 1a5ebbb..864a0b7 100644 --- a/doc/README.org +++ b/doc/README.org @@ -23,12 +23,12 @@ - [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] - The Common Lisp part, which translates the definitions in the - custom C​++ library to CFFI code suitable to be used directly from - Common Lisp. + The Common Lisp part, which translates the definitions of the + custom C​++ library (see below) to CFFI code suitable to be used + directly from Common Lisp. - In addition the targeted C++ Library plus its header files will also - be needed. + In addition the targeted C++ Library (if any) and its header files + will also be needed. ** Custom parts @@ -36,15 +36,15 @@ - A custom C​++ library - This contains the API bindings to the targeted C​++ library to be - exposed to Lisp, package name definitions for the Lisp side, the - Lisp names of functions, methods, member functions, etc... + This contains the C​++ code to be exposed to Lisp, the API + bindings, package name definitions for the Lisp side, the Lisp + names of functions, methods, member functions, etc... - The custom Common Lisp code This contains the code to load the foreign libraries and - initialize the cxx system and package definitions from the C++ lib - to be accessible by Lisp. + initialize the cl-cxx system and package definitions from the C++ + lib to be accessible by Lisp. The following diagram shows the relation of the different parts: [[./block-diagram.svg]] @@ -54,10 +54,17 @@ etc. is *not* done directly on the Lisp side, as it's normally done when directly working with standard CFFI, but rather on the C​++ side, with the translations, name mangling, etc. done automatically - by cl-cxx. + by CLCXX and cl-cxx. +* Examples -Architecture - Block diagram, sequence diagrams for the calls from Lisp to C++ Examples - 2 or 3 end to end examples, with extensive documentation -Future Direction - What is planned next, if anything. E.g. Auto-wrap style wrapping so you can point to a header. Roadmap for the C++ features that are missing. -Help Wanted - what can contributors help with, by skill level. + +* Future Direction + + none yet... + +* Help Wanted + + please send bug reports, code to custom libs using the system, + etc. for expanding the user base. From 7dc3dc5d99443e2e437dc444a7a8f230a34c4c90 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:07:33 +0100 Subject: [PATCH 08/46] misc README --- doc/README.org | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/README.org b/doc/README.org index 864a0b7..e7f1298 100644 --- a/doc/README.org +++ b/doc/README.org @@ -51,10 +51,10 @@ Note that with this design, the definition of functions, structs, - etc. is *not* done directly on the Lisp side, as it's normally done - when directly working with standard CFFI, but rather on the C​++ - side, with the translations, name mangling, etc. done automatically - by CLCXX and cl-cxx. + etc. is *not* done on the Lisp side, as it's normally done when + directly working with standard CFFI, but rather on the C​++ side, + with the translations, name mangling, etc. done automatically by + CLCXX and cl-cxx. * Examples From bddc7bd96da34edf646c9fd6451f6a204b11edc9 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:10:19 +0100 Subject: [PATCH 09/46] misc README --- doc/README.org | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index e7f1298..0490f44 100644 --- a/doc/README.org +++ b/doc/README.org @@ -54,7 +54,9 @@ etc. is *not* done on the Lisp side, as it's normally done when directly working with standard CFFI, but rather on the C​++ side, with the translations, name mangling, etc. done automatically by - CLCXX and cl-cxx. + CLCXX and cl-cxx. The two functions taking care of the cffi + definitions in Common Lisp are =(cxx:init)= and + =(cxx:add-package...)= * Examples From 601929a72cc18a920e14de56799324b83148c205 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:24:35 +0100 Subject: [PATCH 10/46] graphic updated --- doc/README.org | 7 ++++--- doc/block-diagram.svg | 31 ++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/doc/README.org b/doc/README.org index 0490f44..47887ec 100644 --- a/doc/README.org +++ b/doc/README.org @@ -54,12 +54,13 @@ etc. is *not* done on the Lisp side, as it's normally done when directly working with standard CFFI, but rather on the C​++ side, with the translations, name mangling, etc. done automatically by - CLCXX and cl-cxx. The two functions taking care of the cffi - definitions in Common Lisp are =(cxx:init)= and + CLCXX and cl-cxx. The two functions taking care of translating the + C​++ definitions into cffi are =(cxx:init)= and =(cxx:add-package...)= * Examples - + + Following is a full example Examples - 2 or 3 end to end examples, with extensive documentation * Future Direction diff --git a/doc/block-diagram.svg b/doc/block-diagram.svg index 54bc02b..62bbd5f 100644 --- a/doc/block-diagram.svg +++ b/doc/block-diagram.svg @@ -2,9 +2,9 @@ + inkscape:current-layer="layer1" /> + + + + transform="translate(-9.7334579,-50.793281)"> + transform="translate(-9.7334579,-50.793281)"> @@ -224,7 +237,7 @@ Date: Sat, 11 Feb 2023 20:36:13 +0100 Subject: [PATCH 11/46] misc README --- doc/README.org | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/README.org b/doc/README.org index 47887ec..f1ea707 100644 --- a/doc/README.org +++ b/doc/README.org @@ -27,8 +27,8 @@ custom C​++ library (see below) to CFFI code suitable to be used directly from Common Lisp. - In addition the targeted C++ Library (if any) and its header files - will also be needed. + In addition additional existing C++ Libraries and their header files + can be included in case they are targeted. ** Custom parts @@ -36,15 +36,18 @@ - A custom C​++ library - This contains the C​++ code to be exposed to Lisp, the API - bindings, package name definitions for the Lisp side, the Lisp - names of functions, methods, member functions, etc... - + This contains the C​++ code to be exposed to Lisp, custom + definitions or glue code to existing libraries, the API bindings, + package name definitions for the Lisp side, the Lisp names of + functions, methods, member functions, etc... + - The custom Common Lisp code This contains the code to load the foreign libraries and initialize the cl-cxx system and package definitions from the C++ - lib to be accessible by Lisp. + lib to be accessible by Lisp. In addition this code can also + contain macros and such to make the API "lispier", as it is common + for CFFI related packages. The following diagram shows the relation of the different parts: [[./block-diagram.svg]] From 8713cc3dc974b2d8f47b132daf278bd4ace660c9 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:38:29 +0100 Subject: [PATCH 12/46] misc README --- doc/README.org | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/README.org b/doc/README.org index f1ea707..ac20a80 100644 --- a/doc/README.org +++ b/doc/README.org @@ -1,15 +1,17 @@ * Introduction - This library provides an interface to C++ from Common Lisp. It was - inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar functionality as - python's [[https://github.com/pybind/pybind11][pybind11]] for Common Lisp. - + This library provides an interface to C++ from Common Lisp. + As [[https://cffi.common-lisp.dev/][CFFI]] is normally used for C code, there are no builtin functions to deal with C++ extensions from Common Lisp like class definitions, their instantiation, accessing and defining member functions, etc. [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] is an attempt to step in and facilitate easy interfacing from Common Lisp to C++ libs. + It was inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar + functionality as python's [[https://github.com/pybind/pybind11][pybind11]] for Common Lisp. + + * Architecture ** Provided parts From f7cb2cb015e1b3d38a9dc09f8544b26007df5ec2 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:39:41 +0100 Subject: [PATCH 13/46] misc README --- doc/README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index ac20a80..6792f7b 100644 --- a/doc/README.org +++ b/doc/README.org @@ -9,7 +9,7 @@ from Common Lisp to C++ libs. It was inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar - functionality as python's [[https://github.com/pybind/pybind11][pybind11]] for Common Lisp. + functionality as python's [[https://github.com/pybind/pybind11][pybind11]] for the Common Lisp community. * Architecture From eb2381d0215499c03bd9c707cdea8c02eec291d2 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:48:01 +0100 Subject: [PATCH 14/46] started example README --- doc/README.org | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index 6792f7b..8ce7798 100644 --- a/doc/README.org +++ b/doc/README.org @@ -60,11 +60,23 @@ directly working with standard CFFI, but rather on the C​++ side, with the translations, name mangling, etc. done automatically by CLCXX and cl-cxx. The two functions taking care of translating the - C​++ definitions into cffi are =(cxx:init)= and + compiled C​++ definitions into cffi are =(cxx:init)= and =(cxx:add-package...)= * Examples + The =example= folder contains a full example of all needed steps. + The following steps are explained using linux commands. Please + translate them into the corresponding commands for your OS. + + - Copy the folder with all contents to a location of your choice: + +#+BEGIN_SRC bash + +$ cp -R examples /tmp/ + +#+END_SRC bash + Following is a full example Examples - 2 or 3 end to end examples, with extensive documentation From 9e23ecfc3bf6bb8676474eacf22483593b58b116 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:57:00 +0100 Subject: [PATCH 15/46] more README --- doc/README.org | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/README.org b/doc/README.org index 8ce7798..31f2062 100644 --- a/doc/README.org +++ b/doc/README.org @@ -74,9 +74,28 @@ #+BEGIN_SRC bash $ cp -R examples /tmp/ +$ cd /tmp/examples #+END_SRC bash +The necessary files for the custom C++ library are in the subfolder +=custom-lib=. If additional libs are needed, put their header files +into the =custom-lib/include= and add their reference to the file +CMakeLists.txt as in a normal C++ project. See the commented parts of +CMakeLists.txt for an example. + +The C​++ source file is located in =custom-lib/src/my-lib.cpp= Here are +it's contents: + +#+BEGIN_SRC c + +include + +#+END_SRC bash + + +Build the custom C++ library + Following is a full example Examples - 2 or 3 end to end examples, with extensive documentation From e3f08423bf5fd41d78e51d65eed9c06266a3b4ff Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 20:59:27 +0100 Subject: [PATCH 16/46] more README --- doc/README.org | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README.org b/doc/README.org index 31f2062..a8d96dd 100644 --- a/doc/README.org +++ b/doc/README.org @@ -65,8 +65,8 @@ * Examples - The =example= folder contains a full example of all needed steps. - The following steps are explained using linux commands. Please + The =example= folder contains a full example. The necessary steps to + make the example work are explained using linux commands. Please translate them into the corresponding commands for your OS. - Copy the folder with all contents to a location of your choice: From 94afaa4e395539bf88be56b4e95e60a920e7a72b Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:04:11 +0100 Subject: [PATCH 17/46] added README --- doc/README.org | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/README.org b/doc/README.org index a8d96dd..384fdfd 100644 --- a/doc/README.org +++ b/doc/README.org @@ -6,12 +6,13 @@ to deal with C++ extensions from Common Lisp like class definitions, their instantiation, accessing and defining member functions, etc. [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] is an attempt to step in and facilitate easy interfacing - from Common Lisp to C++ libs. + from Common Lisp to C​++ libs. It uses CFFI under the hood, but + generates its definitions automagically from C​++ code provided by + the user. It was inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar functionality as python's [[https://github.com/pybind/pybind11][pybind11]] for the Common Lisp community. - * Architecture ** Provided parts From ba1bcbbe690c3dce600702fd023f1b3adcbed1e0 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:05:19 +0100 Subject: [PATCH 18/46] added README --- doc/README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index 384fdfd..c731805 100644 --- a/doc/README.org +++ b/doc/README.org @@ -10,7 +10,7 @@ generates its definitions automagically from C​++ code provided by the user. - It was inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar + The code was inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar functionality as python's [[https://github.com/pybind/pybind11][pybind11]] for the Common Lisp community. * Architecture From 07f4f4b1e0b9f84547c47b23dbff2b4139610fc3 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:06:22 +0100 Subject: [PATCH 19/46] added README --- doc/README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index c731805..f6acfcf 100644 --- a/doc/README.org +++ b/doc/README.org @@ -7,7 +7,7 @@ their instantiation, accessing and defining member functions, etc. [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] is an attempt to step in and facilitate easy interfacing from Common Lisp to C​++ libs. It uses CFFI under the hood, but - generates its definitions automagically from C​++ code provided by + generates its definitions automagically from C​++ code written by the user. The code was inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar From a1fc793f148ab58f0c509c1f938214260c076065 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:07:31 +0100 Subject: [PATCH 20/46] added README --- doc/README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index f6acfcf..2d4d856 100644 --- a/doc/README.org +++ b/doc/README.org @@ -22,7 +22,7 @@ - [[https://github.com/Islam0mar/CLCXX][CLCXX]] A C​++ library which gets compiled once and installed systemwide, - visible by the C++ compiler. + visible by the C++ toolchain. - [[https://github.com/Islam0mar/cl-cxx][cl-cxx]] From 22faf93b36ca6f8796c96092d925e17d9b896fa2 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:09:08 +0100 Subject: [PATCH 21/46] misc README --- doc/README.org | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/README.org b/doc/README.org index 2d4d856..53381bd 100644 --- a/doc/README.org +++ b/doc/README.org @@ -53,6 +53,7 @@ for CFFI related packages. The following diagram shows the relation of the different parts: + [[./block-diagram.svg]] From 1cbe41061dcb7bc5bad36f12c9004e4b5a95594b Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:10:48 +0100 Subject: [PATCH 22/46] misc README --- doc/README.org | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/README.org b/doc/README.org index 53381bd..f00789f 100644 --- a/doc/README.org +++ b/doc/README.org @@ -61,9 +61,9 @@ etc. is *not* done on the Lisp side, as it's normally done when directly working with standard CFFI, but rather on the C​++ side, with the translations, name mangling, etc. done automatically by - CLCXX and cl-cxx. The two functions taking care of translating the - compiled C​++ definitions into cffi are =(cxx:init)= and - =(cxx:add-package...)= + CLCXX and cl-cxx. The two Common Lisp functions taking care of + translating the compiled C​++ definitions into cffi are =(cxx:init)= + and =(cxx:add-package...)= * Examples From b3bedf6271650c964b43d27474c8e71b499f3ccd Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:11:40 +0100 Subject: [PATCH 23/46] misc README --- doc/README.org | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/README.org b/doc/README.org index f00789f..ed7b454 100644 --- a/doc/README.org +++ b/doc/README.org @@ -80,11 +80,11 @@ $ cd /tmp/examples #+END_SRC bash -The necessary files for the custom C++ library are in the subfolder -=custom-lib=. If additional libs are needed, put their header files -into the =custom-lib/include= and add their reference to the file -CMakeLists.txt as in a normal C++ project. See the commented parts of -CMakeLists.txt for an example. +The necessary files for the custom C++ library are located in the +subfolder =custom-lib=. If additional libs are needed, put their +header files into the =custom-lib/include= and add their reference to +the file CMakeLists.txt as in a normal C++ project. See the commented +parts of CMakeLists.txt for an example. The C​++ source file is located in =custom-lib/src/my-lib.cpp= Here are it's contents: From eddf35eceffc925f51a43b04f16b4e707ef0292e Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:21:00 +0100 Subject: [PATCH 24/46] misc README --- doc/README.org | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/doc/README.org b/doc/README.org index ed7b454..e8545af 100644 --- a/doc/README.org +++ b/doc/README.org @@ -67,9 +67,10 @@ * Examples - The =example= folder contains a full example. The necessary steps to - make the example work are explained using linux commands. Please - translate them into the corresponding commands for your OS. + The =example= folder contains a full example. It can be used as a + skeleton for a new project. The necessary steps to make the example + work are explained using linux commands. Please translate them into + the corresponding commands for your OS. - Copy the folder with all contents to a location of your choice: @@ -78,13 +79,13 @@ $ cp -R examples /tmp/ $ cd /tmp/examples -#+END_SRC bash +#+END_SRC The necessary files for the custom C++ library are located in the subfolder =custom-lib=. If additional libs are needed, put their -header files into the =custom-lib/include= and add their reference to -the file CMakeLists.txt as in a normal C++ project. See the commented -parts of CMakeLists.txt for an example. +header files into the =custom-lib/include= folder and add their +reference to the file CMakeLists.txt as in a normal C++ project. See +the commented parts of CMakeLists.txt for an example. The C​++ source file is located in =custom-lib/src/my-lib.cpp= Here are it's contents: @@ -93,11 +94,24 @@ it's contents: include -#+END_SRC bash +#+END_SRC Build the custom C++ library +#+BEGIN_SRC bash + +$ cd /tmp/examples/my-lib +$ mkdir build +$ cd build +$ cmake .. +$ make + +#+END_SRC + +This should compile a shared library named =myLib.so= and put it into +the folder =/tmp/example/my-lib/lib/= + Following is a full example Examples - 2 or 3 end to end examples, with extensive documentation From 513935142f8d5602473f9c0957e70860a22ecff3 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:23:40 +0100 Subject: [PATCH 25/46] misc README --- doc/README.org | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/README.org b/doc/README.org index e8545af..bad95fb 100644 --- a/doc/README.org +++ b/doc/README.org @@ -109,8 +109,9 @@ $ make #+END_SRC -This should compile a shared library named =myLib.so= and put it into -the folder =/tmp/example/my-lib/lib/= +This should compile a shared library named =myLib.so= (maybe with +another extension according to your OS) and put it into the folder +=/tmp/example/my-lib/lib/= Following is a full example Examples - 2 or 3 end to end examples, with extensive documentation From bdfdfe0ec696df2afb5ebc4b7a5e20efb444dea6 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:26:45 +0100 Subject: [PATCH 26/46] misc README --- doc/README.org | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README.org b/doc/README.org index bad95fb..c0cde42 100644 --- a/doc/README.org +++ b/doc/README.org @@ -30,8 +30,8 @@ custom C​++ library (see below) to CFFI code suitable to be used directly from Common Lisp. - In addition additional existing C++ Libraries and their header files - can be included in case they are targeted. + In addition existing C++ Libraries and their header files can be + included in case they are targeted. ** Custom parts From 574aa08a1da9cc2a62086df8c7dc3595911aa996 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:27:32 +0100 Subject: [PATCH 27/46] misc --- doc/README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index c0cde42..7ac2024 100644 --- a/doc/README.org +++ b/doc/README.org @@ -88,7 +88,7 @@ reference to the file CMakeLists.txt as in a normal C++ project. See the commented parts of CMakeLists.txt for an example. The C​++ source file is located in =custom-lib/src/my-lib.cpp= Here are -it's contents: +its contents: #+BEGIN_SRC c From 4e96fc4a37f80bc396e6e7436d528961bf8c5393 Mon Sep 17 00:00:00 2001 From: ormf Date: Sat, 11 Feb 2023 21:33:41 +0100 Subject: [PATCH 28/46] misc --- doc/README.org | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/README.org b/doc/README.org index 7ac2024..6c3769e 100644 --- a/doc/README.org +++ b/doc/README.org @@ -58,10 +58,10 @@ Note that with this design, the definition of functions, structs, - etc. is *not* done on the Lisp side, as it's normally done when - directly working with standard CFFI, but rather on the C​++ side, - with the translations, name mangling, etc. done automatically by - CLCXX and cl-cxx. The two Common Lisp functions taking care of + etc. is *not* done by the user on the Lisp side, as it is the case + when working with standard CFFI directly, but rather on the C​++ + side, with the translations, name mangling, etc. done automatically + by CLCXX and cl-cxx. The two Common Lisp functions taking care of translating the compiled C​++ definitions into cffi are =(cxx:init)= and =(cxx:add-package...)= @@ -84,8 +84,9 @@ $ cd /tmp/examples The necessary files for the custom C++ library are located in the subfolder =custom-lib=. If additional libs are needed, put their header files into the =custom-lib/include= folder and add their -reference to the file CMakeLists.txt as in a normal C++ project. See -the commented parts of CMakeLists.txt for an example. +reference to the file =/tmp/example/custom-lib/CMakeLists.txt= as in a +normal C++ project. See the commented parts of CMakeLists.txt for an +example. The C​++ source file is located in =custom-lib/src/my-lib.cpp= Here are its contents: From ea48d557d4d5becd1104c1dc89d5079bff5c21a7 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 09:11:52 +0100 Subject: [PATCH 29/46] added example. --- doc/README.org | 98 ++- example/custom-lib/CMakeLists.txt | 21 + example/custom-lib/include/clcxx/clcxx.hpp | 14 + .../custom-lib/include/clcxx/clcxx_config.hpp | 25 + .../custom-lib/include/clcxx/hash_type.hpp | 149 ++++ example/custom-lib/include/clcxx/memory.hpp | 39 ++ example/custom-lib/include/clcxx/package.hpp | 638 ++++++++++++++++++ .../include/clcxx/type_conversion.hpp | 617 +++++++++++++++++ example/custom-lib/src/my-lib.cpp | 80 +++ example/my-cl-lib.asd | 14 + example/package.lisp | 7 + 11 files changed, 1693 insertions(+), 9 deletions(-) create mode 100644 example/custom-lib/CMakeLists.txt create mode 100755 example/custom-lib/include/clcxx/clcxx.hpp create mode 100755 example/custom-lib/include/clcxx/clcxx_config.hpp create mode 100644 example/custom-lib/include/clcxx/hash_type.hpp create mode 100644 example/custom-lib/include/clcxx/memory.hpp create mode 100755 example/custom-lib/include/clcxx/package.hpp create mode 100755 example/custom-lib/include/clcxx/type_conversion.hpp create mode 100644 example/custom-lib/src/my-lib.cpp create mode 100644 example/my-cl-lib.asd create mode 100644 example/package.lisp diff --git a/doc/README.org b/doc/README.org index 6c3769e..f322a3b 100644 --- a/doc/README.org +++ b/doc/README.org @@ -72,6 +72,7 @@ work are explained using linux commands. Please translate them into the corresponding commands for your OS. +** Setup - Copy the folder with all contents to a location of your choice: #+BEGIN_SRC bash @@ -83,22 +84,101 @@ $ cd /tmp/examples The necessary files for the custom C++ library are located in the subfolder =custom-lib=. If additional libs are needed, put their -header files into the =custom-lib/include= folder and add their -reference to the file =/tmp/example/custom-lib/CMakeLists.txt= as in a -normal C++ project. See the commented parts of CMakeLists.txt for an -example. +header files into the =custom-lib/include= folder or optionally into +=custom-lib/modules= and add their reference to the file +=/tmp/example/custom-lib/CMakeLists.txt= as in a normal C++ project. -The C​++ source file is located in =custom-lib/src/my-lib.cpp= Here are -its contents: +** The C​++ source file + + The C​++ source file is located in =custom-lib/src/my-lib.cpp= Here + are its contents: #+BEGIN_SRC c -include +#include +#include "clcxx/clcxx.hpp" + +// standard C function definitions: + +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 x) { return x; } +std::string hi(char* s) { return std::string("hi, " + std::string(s)); } +void ref_int(int& x) { x += 30; } + +// C++ class definition with members and method: class xx { public: + +class xx { + public: + xx(int xx, int yy) : y(yy), x(xx) {} + std::string greet() { return "Hello, World"; } + int y; + int x; +}; +void ref_class(xx& x) { x.y = 1000000; } + +// definitions of the API exposure to Common Lisp: +// +// CLCXX_PACKAGE defines a Common Lisp package which can be loaded by +// Common Lisp with the command "(add-package "TEST" "CL-TEST")" +// +// The first argument of add-package is the name of the Package +// defined with CLCXX_PACKAGE in the cpp file. +// +// The second argument of add-package is the name of the common-lisp +// package to define. +// +// pack is the variable name referencing the package. +// +// The methods are: +// +// .defun (std::string "lisp-fn-name", fn-pointer c-fn) +// +// defines the Common Lisp function #'cl-test:lisp-fn-name to call +// "c-fn" from the code above. +// +// .defclass ("lisp-class-name") +// +// defines a class accessor for Common Lisp +// +// .member("cl-name", class-pointer c-name) +// +// define a member of the class +// +// .defmethod("cl-name", fn-pointer c-method) +// +// the class's setter and getter functions are also defined this way: +// +// .defmethod("foo.x", F_PTR([](xx x){return x.x;})) +// +// defines the methods #'cl-test:foo.x as getter and +// #'cl-test:foo.x.set as setter for the member x of class xx. +// +// .constructor defines a constructor function for Common-lisp +// +// .constructor("create-xx"); defines a Common Lisp +// constructor function #'cl-test:create-xx with two integer +// arguments. + +CLCXX_PACKAGE TEST(clcxx::Package& pack) { + pack.defun("hi", F_PTR(&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") + .member("y", &xx::y) + .defmethod("foo", F_PTR(&xx::greet)) + .defmethod("foo.x", F_PTR([](xx x){return x.x;})) + .constructor("create-xx"); +} #+END_SRC - -Build the custom C++ library +** Building the Library #+BEGIN_SRC bash diff --git a/example/custom-lib/CMakeLists.txt b/example/custom-lib/CMakeLists.txt new file mode 100644 index 0000000..70c0319 --- /dev/null +++ b/example/custom-lib/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.0) + +# set the name of the library to compile +project(MyLib) + +# set the directory to save the compiled lib +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) + +# optionally include the code for additional libs +# include(modules/link/AbletonLinkConfig.cmake) + +# add the include directory for clcxx +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + +# set the cpp file to conpile +add_library(MyLib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/src/my-lib.cpp) + +# optionally add libraries needed for linking +# target_link_libraries(AbletonLink PRIVATE Ableton::Link) + + diff --git a/example/custom-lib/include/clcxx/clcxx.hpp b/example/custom-lib/include/clcxx/clcxx.hpp new file mode 100755 index 0000000..e59b7e4 --- /dev/null +++ b/example/custom-lib/include/clcxx/clcxx.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "package.hpp" +#include "type_conversion.hpp" diff --git a/example/custom-lib/include/clcxx/clcxx_config.hpp b/example/custom-lib/include/clcxx/clcxx_config.hpp new file mode 100755 index 0000000..e516869 --- /dev/null +++ b/example/custom-lib/include/clcxx/clcxx_config.hpp @@ -0,0 +1,25 @@ +#pragma once + +#ifdef _WIN32 +#ifdef CLCXX_EXPORTS +#define CLCXX_API __declspec(dllexport) +#else +#define CLCXX_API __declspec(dllimport) +#endif +#define CLCXX_ONLY_EXPORTS __declspec(dllexport) +#else +#define CLCXX_API __attribute__((visibility("default"))) +#define CLCXX_ONLY_EXPORTS CLCXX_API +#endif + +#define CLCXX_VERSION_MAJOR 1 +#define CLCXX_VERSION_MINOR 0 +#define CLCXX_VERSION_PATCH 0 + +// From +// https://stackoverflow.com/questions/5459868/concatenate-int-to-string-using-c-preprocessor +#define __CLCXX_STR_HELPER(x) #x +#define __CLCXX_STR(x) __CLCXX_STR_HELPER(x) +#define CLCXX_VERSION_STRING \ + __CLCXX_STR(CLCXX_VERSION_MAJOR) \ + "." __CLCXX_STR(CLCXX_VERSION_MINOR) "." __CLCXX_STR(CLCXX_VERSION_PATCH) diff --git a/example/custom-lib/include/clcxx/hash_type.hpp b/example/custom-lib/include/clcxx/hash_type.hpp new file mode 100644 index 0000000..1ebd778 --- /dev/null +++ b/example/custom-lib/include/clcxx/hash_type.hpp @@ -0,0 +1,149 @@ +#pragma once + +#include +#include + +namespace clcxx { + +using SizeT = std::uint_fast32_t; + +// C++ type name from: +// https://stackoverflow.com/questions/81870/is-it-possible-to-prSize-a-variables-type-in-standard-c/56766138#56766138 +template +constexpr auto TypeName() { + std::string_view name, prefix, suffix; +#ifdef __clang__ + name = __PRETTY_FUNCTION__; + prefix = "auto utils::TypeName() [T = "; + suffix = "]"; +#elif defined(__GNUC__) + name = __PRETTY_FUNCTION__; + prefix = "constexpr auto utils::TypeName() [with T = "; + suffix = "]"; +#elif defined(_MSC_VER) + name = __FUNCSIG__; + prefix = "auto __cdecl utils::TypeName<"; + suffix = ">(void)"; +#endif + name.remove_prefix(prefix.size()); + name.remove_suffix(suffix.size()); + return name; +} + +// compile time string hash for types from: +// https://roartindon.blogspot.com/2014/10/compile-time-murmur-hash-in-c.html +namespace detail { +template +struct Sequence {}; +template +struct CreateSequence : CreateSequence {}; +template +struct CreateSequence<0, S...> { + typedef Sequence Type; +}; + +constexpr SizeT UpdateHash1(SizeT k) { return k * 0xcc9e2d51; } +constexpr SizeT UpdateHash2(SizeT k) { return (k << 15) | (k >> (32 - 15)); } +constexpr SizeT UpdateHash3(SizeT k) { return k * 0x1b873593; } +constexpr SizeT UpdateHash4(SizeT hash, SizeT block) { return hash ^ block; } +constexpr SizeT UpdateHash5(SizeT hash) { + return ((hash << 13) | (hash >> (32 - 13))) * 5 + 0xe6546b64; +} + +constexpr SizeT UpdateHash(SizeT hash, SizeT block) { + return UpdateHash5( + UpdateHash4(hash, UpdateHash3(UpdateHash2(UpdateHash1(block))))); +} + +constexpr SizeT UpdateLastHash(SizeT hash, SizeT block) { + return UpdateHash4(hash, UpdateHash3(UpdateHash2(UpdateHash1(block)))); +} + +template +constexpr SizeT CalculateHashRounds(SizeT seed, C... c); + +template <> +constexpr SizeT CalculateHashRounds(SizeT seed) { + return seed; +} + +template <> +constexpr SizeT CalculateHashRounds(SizeT seed, char c0) { + return UpdateLastHash(seed, std::uint8_t(c0)); +} + +template <> +constexpr SizeT CalculateHashRounds(SizeT seed, char c0, char c1) { + return UpdateLastHash(seed, std::uint8_t(c0) | std::uint8_t(c1) << 8); +} + +template <> +constexpr SizeT CalculateHashRounds(SizeT seed, char c0, char c1, char c2) { + return UpdateLastHash( + seed, std::uint8_t(c0) | std::uint8_t(c1) << 8 | std::uint8_t(c2) << 16); +} + +template +constexpr SizeT CalculateHashRounds(SizeT seed, char c0, char c1, char c2, + char c3, C... c) { + return CalculateHashRounds( + UpdateHash(seed, std::uint8_t(c0) | std::uint8_t(c1) << 8 | + std::uint8_t(c2) << 16 | std::uint8_t(c3) << 24), + c...); +} + +constexpr SizeT CalculateFinalHash1(SizeT h, SizeT length) { + return h ^ length; +} + +constexpr SizeT CalculateFinalHash2(SizeT h) { return h ^ (h >> 16); } + +constexpr SizeT CalculateFinalHash3(SizeT h) { return h * 0x85ebca6b; } + +constexpr SizeT CalculateFinalHash4(SizeT h) { return h ^ (h >> 13); } + +constexpr SizeT CalculateFinalHash5(SizeT h) { return h * 0xc2b2ae35; } + +constexpr SizeT CalculateFinalHash6(SizeT h) { return h ^ (h >> 16); } + +constexpr SizeT CalculateFinalHash(SizeT h, SizeT length) { + return CalculateFinalHash6( + CalculateFinalHash5(CalculateFinalHash4(CalculateFinalHash3( + CalculateFinalHash2(CalculateFinalHash1(h, length)))))); +} + +// This is used to convert from calling const char (&s)[N] +// To CalculateHashRounds(seed, s[0], s[1], s[2], s[3], ... ) +template +constexpr SizeT Unpack(unsigned seed, const char (&s)[N], Sequence) { + return CalculateHashRounds(seed, s[S]...); +} +// This is used to convert from calling std::string_view +// To CalculateHashRounds(seed, s[0], s[1], s[2], s[3], ... ) +template +constexpr SizeT Unpack(unsigned seed, const std::string_view &s, + Sequence) { + return CalculateHashRounds(seed, s[S]...); +} + +template +constexpr SizeT murmur3_32(const char (&s)[N], SizeT seed = 0) { + return CalculateFinalHash( + Unpack(seed, s, typename CreateSequence::Type()), N - 1); +} + +template +constexpr SizeT murmur3_32(const std::string_view &s, SizeT seed = 0) { + return CalculateFinalHash( + Unpack(seed, s, typename CreateSequence::Type()), N); +} + +} // namespace detail + +template +constexpr auto Hash32TypeName() { + constexpr auto s = TypeName(); + return detail::murmur3_32(std::move(s)); +} + +} // namespace clcxx diff --git a/example/custom-lib/include/clcxx/memory.hpp b/example/custom-lib/include/clcxx/memory.hpp new file mode 100644 index 0000000..cbc4aa2 --- /dev/null +++ b/example/custom-lib/include/clcxx/memory.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace clcxx { + +// allocator +constexpr auto BUF_SIZE = 1 * 1024 * 1024; + +class VerboseResource : public std::pmr::memory_resource { + public: + explicit VerboseResource(std::pmr::memory_resource *upstream_resource) + : upstream_resource_(upstream_resource), num_of_bytes_allocated(0) {} + + size_t get_num_of_bytes_allocated() { return num_of_bytes_allocated; } + + private: + void *do_allocate(size_t bytes, size_t alignment) override { + num_of_bytes_allocated += bytes; + return upstream_resource_->allocate(bytes, alignment); + } + + void do_deallocate(void *p, size_t bytes, size_t alignment) override { + num_of_bytes_allocated -= bytes; + upstream_resource_->deallocate(p, bytes, alignment); + } + + [[nodiscard]] bool do_is_equal( + const memory_resource &other) const noexcept override { + return this == &other; + } + + std::pmr::memory_resource *upstream_resource_; + size_t num_of_bytes_allocated; +}; + +[[nodiscard]] VerboseResource &MemPool(); + +} // namespace clcxx diff --git a/example/custom-lib/include/clcxx/package.hpp b/example/custom-lib/include/clcxx/package.hpp new file mode 100755 index 0000000..39bf52c --- /dev/null +++ b/example/custom-lib/include/clcxx/package.hpp @@ -0,0 +1,638 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "type_conversion.hpp" + +/// helpper for Import function +#define F_PTR(...) \ + reinterpret_cast(clcxx::Import([&]() { return __VA_ARGS__; })), \ + __VA_ARGS__ + +namespace clcxx { + +extern "C" typedef struct { + char *name; + char *super_classes; + char *slot_names; + char *slot_types; + void (*constructor)(); // null := pod class + void (*destructor)(); // null := pod class +} ClassInfo; + +extern "C" typedef struct { + char *name; + bool method_p; + char *class_obj; + void (*func_ptr)(); + char *arg_types; + char *return_type; +} FunctionInfo; + +extern "C" typedef struct { + char *name; + char *value; +} ConstantInfo; + +extern "C" typedef union { + FunctionInfo Func; + ClassInfo Class; + ConstantInfo Const; +} MetaData; + +class CLCXX_API Package; + +inline void LispError(const char *error); + +namespace detail { +template +struct is_functional : public std::false_type {}; +template +struct is_functional> : public std::true_type {}; + +template +inline constexpr bool is_functional_v = is_functional::value; + +template +ToLisp_t DoApply(ToLisp_t... args) { + try { + if constexpr (std::is_invocable_v...>) { + if constexpr (std::is_same_v, void>) { + std::invoke(invocable_pointer, ToCpp(std::move(args))...); + return; + } else { + return ToLisp( + std::invoke(invocable_pointer, ToCpp(std::move(args))...)); + } + } else { + if constexpr (std::is_same_v, void>) { + std::invoke(*invocable_pointer, ToCpp(std::move(args))...); + return; + } else { + return ToLisp( + std::invoke(*invocable_pointer, ToCpp(std::move(args))...)); + } + } + } catch (const std::exception &err) { + LispError(err.what()); + } + return ToLisp_t(); +} + +template +constexpr auto ResolveInvocable(std::function *) { + return &DoApply, + std::remove_const_t...>; +} +template +constexpr auto ResolveInvocable(R (*)(Args...)) { + return &DoApply, + std::remove_const_t...>; +} + +template +constexpr auto ResolveInvocable(R (CT::*)(Args...)) { + return &DoApply, std::remove_const_t, + std::remove_const_t...>; +} +template +constexpr auto ResolveInvocable(R (CT::*)(Args...) const) { + return &DoApply, std::remove_const_t, + std::remove_const_t...>; +} +template +constexpr auto ResolveInvocableLambda(R (LambdaT::*)(Args...) const) { + return &DoApply, + std::remove_const_t...>; +} + +/// mutable lambda +template +constexpr auto ResolveInvocableLambda(R (LambdaT::*)(Args...)) { + return &DoApply, + std::remove_const_t...>; +} +template +constexpr auto ResolveInvocable( + std::enable_if_t< + std::is_class_v> && + !detail::is_functional_v< + std::remove_pointer_t>, + decltype(lambda_ptr)> + p) { + return ResolveInvocableLambda, lambda_ptr>( + &std::remove_pointer_t::operator()); +} + +template +inline constexpr auto DecayThenResolve() { + return ResolveInvocable>(x)>( + std::forward>(x)); +} +} // namespace detail + +/** + * @brief import a function, method, lambda, or std_function + * + * @details takes a lambda input such as this: + * [&](){return ;} + * and convert it to the address of thunk_function which + * takes arguments to cxx function. + * when method is imported, you have to pass object as + * the first argument. + * example 1: + * given: int foo(float f){return (int)f;} + * import: Import([&]() { return foo; }) + * that would give the address of the thunk function. + * example 2: + * given: int foo(float f){return (int)f;} + * import: Import([&]() { return foo; })(5.123) + * that would execute foo(5.123) and return 5. + * could be invoked with Import([&]() { return &foo; }) + * use cases: + * Import([&]() { return &MyClass::foo; })(foo_object, args...) + * Import([&]() { return &MyClass::operator(); })(foo_object) + * Import([&]() { return []() { return "Hello, World\n"; }; })() + * + * @param lambda class + * + * @return function pointer to the thunk fuction + */ +template +inline auto Import(T lambda) { + if constexpr (std::is_class_v) { + static auto w = lambda(); + constexpr auto res = detail::DecayThenResolve<&w>(); + return res; + } else { + constexpr auto res = detail::DecayThenResolve(); + return res; + } +} + +namespace detail { + +char *str_dup(const char *src); +char *str_append(char *old_str, const char *src); + +template +void remove_c_strings(T obj); + +// Base class to specialize for constructor +template +CppT *CppConstructor(Args... args) { + auto obj_ptr = static_cast( + MemPool().allocate(sizeof(CppT), std::alignment_of_v)); + ::new (obj_ptr) CppT(args...); + return obj_ptr; +} + +using FuncPtr = void (*)(); +template +struct CreateClass { + inline FuncPtr operator()() { + return reinterpret_cast( + clcxx::Import([&]() { return &CppConstructor; })); + } +}; + +template +struct CreateClass { + inline FuncPtr operator()() { return nullptr; } +}; + +template +void free_obj_ptr(void *ptr) { + auto obj_ptr = static_cast(ptr); + obj_ptr->~T(); + MemPool().deallocate(ptr, sizeof(T), std::alignment_of_v); +} + +/// handle POD class +template +std::string arg_type_pod_fix() { + using T = std::remove_cv_t; + auto return_type = LispType(); // case 1 + if constexpr (internal::is_pod_struct_v) { + if (::clcxx::pod_class_name() == "") { + throw std::runtime_error("Pod class " + std::string(TypeName()) + + " isn't registered and it is passed by value"); + } + } + return return_type; +} + +/// Make a string with the types in the variadic template parameter pack +template +std::string arg_types_string() { + std::vector vec = {arg_type_pod_fix()...}; + std::string s; + for (auto arg_types : vec) { + s.append(arg_types); + s.append("+"); + } + return s; +} + +/// Make a string with the super classes in the variadic template parameter +/// pack +template +std::string super_classes_string() { + std::vector vec = {general_class_name()...}; + std::string s; + for (auto super_types : vec) { + s.append(super_types); + s.append("+"); + } + return s; +} +} // namespace detail + +/// Registry containing different packages +class CLCXX_API PackageRegistry { + public: + /// Create a package and register it + Package &create_package(std::string lpack); + + auto get_package_iter(std::string pack) const { + const auto iter = p_packages.find(pack); + if (iter == p_packages.end()) { + throw std::runtime_error("Pack with name " + pack + + " was not found in registry"); + } + return iter; + } + + bool has_package(std::string lpack) const { + return p_packages.find(lpack) != p_packages.end(); + } + + void remove_package(std::string lpack); + + using Iter = std::map>::iterator; + [[nodiscard]] Iter remove_package(Iter iter); + + bool has_current_package() { return p_current_package != nullptr; } + Package ¤t_package(); + void reset_current_package() { p_current_package = nullptr; } + + ~PackageRegistry() { + for (auto it = begin(p_packages); it != end(p_packages);) { + it = remove_package(it); + } + } + + PackageRegistry() + : p_error_handler_callback(nullptr), + p_meta_data_handler_callback(nullptr) {} + + void set_error_handler(void (*callback)(char *)) { + p_error_handler_callback = callback; + } + + void handle_error(char *err_msg) { p_error_handler_callback(err_msg); } + + void set_meta_data_handler(void (*callback)(MetaData *, uint8_t)) { + p_meta_data_handler_callback = callback; + } + + void send_data(MetaData *M, uint8_t n) { p_meta_data_handler_callback(M, n); } + + private: + std::map> p_packages; + Package *p_current_package = nullptr; + void (*p_error_handler_callback)(char *); + void (*p_meta_data_handler_callback)(MetaData *, uint8_t); +}; + +CLCXX_API PackageRegistry ®istry(); + +inline void LispError(const char *error) { + clcxx::registry().handle_error(const_cast(error)); +} + +template +class ClassWrapper; +template +class PodClassWrapper; + +/// Store all exposed C++ functions associated with a package +class CLCXX_API Package { + public: + explicit Package(std::string cl_pack) : p_cl_pack(cl_pack) {} + + /// Define a new function base + template + void defun(const std::string &name, void (*func_ptr)(), T &&functor, + bool is_method = false, const char *class_name = "") { + defun(name, std::forward(functor), is_method, class_name, func_ptr); + } + + /// Add a class type + template + ClassWrapper defclass(const std::string &name, s_classes...) { + static_assert(!internal::is_pod_struct_v, + "Use defcstruct for pod class types."); + auto iter = general_class_name.find(Hash32TypeName()); + if (iter != general_class_name.end()) { + throw std::runtime_error("Class with name " + name + + " was already defined in the package, or maybe " + "murmur3 string collision!"); + } + general_class_name[Hash32TypeName()] = name; + + ClassInfo c_info; + c_info.constructor = detail::CreateClass()(); + c_info.destructor = reinterpret_cast(detail::free_obj_ptr); + c_info.slot_types = nullptr; + c_info.slot_names = nullptr; + c_info.name = detail::str_dup(name.c_str()); + c_info.super_classes = + detail::str_dup(detail::super_classes_string().c_str()); + // Store data + p_classes_meta_data.push_back(c_info); + return ClassWrapper(*this); + } + + template + PodClassWrapper defcstruct(const std::string &name) { + static_assert(internal::is_pod_struct_v, + "defcstruct can be used for pod class types only, you should " + "defclass."); + auto iter = pod_class_name.find(Hash32TypeName()); + if (iter != pod_class_name.end()) { + throw std::runtime_error("struct with name " + name + + " was already defined in the package, or maybe " + "murmur3 string collision!" + + iter->second); + } + pod_class_name[Hash32TypeName()] = name; + + ClassInfo c_info; + c_info.constructor = nullptr; + c_info.destructor = nullptr; + c_info.slot_types = nullptr; + c_info.slot_names = nullptr; + c_info.name = detail::str_dup(name.c_str()); + c_info.super_classes = nullptr; + // Store data + p_classes_meta_data.push_back(c_info); + return PodClassWrapper(*this); + } + + /// Set a global constant value at the package level + template + void defconstant(const std::string &name, T &&value) { + ConstantInfo const_info; + const_info.name = detail::str_dup(name.c_str()); + const_info.value = detail::str_dup(std::to_string(value).c_str()); + p_constants.push_back(const_info); + } + + std::string name() const { return p_cl_pack; } + + const std::unordered_map &general_classes() const { + return general_class_name; + } + + const std::unordered_map &pod_classes() const { + return pod_class_name; + } + + std::vector &classes_meta_data() { return p_classes_meta_data; } + std::vector &functions_meta_data() { + return p_functions_meta_data; + } + std::vector &constants_meta_data() { return p_constants; } + + private: + /// Define a new function + template + void defun(const std::string &name, std::function, bool is_method, + const char *class_name, void (*func_ptr)()) { + FunctionInfo f_info; + f_info.name = detail::str_dup(name.c_str()); + f_info.method_p = is_method; + f_info.class_obj = detail::str_dup(class_name); + f_info.func_ptr = func_ptr; + f_info.arg_types = + detail::str_dup(detail::arg_types_string().c_str()); + f_info.return_type = detail::str_dup(detail::arg_type_pod_fix().c_str()); + // store data + p_functions_meta_data.push_back(f_info); + } + + /// Define a new function. Overload for pointers + template + void defun(const std::string &name, R (*f)(Args...), bool is_method, + const char *class_name, void (*func_ptr)()) { + defun(name, std::function(f), is_method, class_name, func_ptr); + } + + /// Define a new function. Overload for lambda + template + void defun(const std::string &name, LambdaT &&lambda, bool is_method, + const char *class_name, void (*func_ptr)() // , + // std::enable_if_t, + // bool> = true + ) { + static_assert(!std::is_member_function_pointer_v, + "Use defmethod for member functions"); + add_lambda(name, std::forward(lambda), &LambdaT::operator(), + is_method, class_name, func_ptr); + } + + template + void add_lambda(const std::string &name, LambdaT &&lambda, + R (LambdaT::*)(ArgsT...) const, bool is_method, + const char *class_name, void (*func_ptr)()) { + defun(name, std::function(std::forward(lambda)), + is_method, class_name, func_ptr); + } + + template + void add_lambda(const std::string &name, LambdaT &&lambda, + R (LambdaT::*)(ArgsT...), bool is_method, + const char *class_name, void (*func_ptr)()) { + defun(name, std::function(std::forward(lambda)), + is_method, class_name, func_ptr); + } + + std::string p_cl_pack; + std::vector p_classes_meta_data; + std::vector p_functions_meta_data; + std::vector p_constants; + std::unordered_map general_class_name; + std::unordered_map pod_class_name; + template + friend class PodClassWrapper; + template + friend class ClassWrapper; +}; + +// Helper class to wrap type methods +template +class PodClassWrapper { + public: + typedef T type; + + explicit PodClassWrapper(Package &pack) : p_package(pack) {} + + // Add public member >> readwrite + template + PodClassWrapper &member(const std::string &name, MemberT CT::*pm) { + check_member_and_append_its_slots(name, pm); + return *this; + } + + // Access to the module + Package &package() { return p_package; } + + private: + template + constexpr void check_member_and_append_its_slots(const std::string &name, + MemberT CT::*) { + static_assert(std::is_base_of::value, + "member() requires a class member (or base class member)"); + auto &curr_class = p_package.p_classes_meta_data.back(); + + curr_class.slot_types = detail::str_append( + curr_class.slot_types, std::string(LispType() + "+").c_str()); + curr_class.slot_names = detail::str_append(curr_class.slot_names, + std::string(name + "+").c_str()); + } + + Package &p_package; +}; + +template +class ClassWrapper { + public: + typedef T type; + + explicit ClassWrapper(Package &pack) : p_package(pack) {} + + /// Add a constructor with the given argument types + template + ClassWrapper &constructor(std::string name = "") { + auto &curr_class = p_package.p_classes_meta_data.back(); + + if (name == "") + // Use name as a flag + name = std::string("create-" + std::string(curr_class.name) + + std::to_string(sizeof...(Args))); + + p_package.defun(name, F_PTR(detail::CppConstructor), false, + curr_class.name); + return *this; + } + + /// Define a member function + template + ClassWrapper &defmethod(const std::string &name, void (*func_ptr)(), + FuncT &&functor) { + defmethod(name, std::forward(functor), func_ptr); + return *this; + } + + // Add public member >> readwrite + template + ClassWrapper &member(const std::string &name, MemberT CT::*pm) { + check_member_and_append_its_slots(name, pm); + defmethod(std::string(name + ".get"), + F_PTR([pm](const T &c) -> const MemberT { return c.*pm; })); + defmethod(std::string(name + ".set"), + F_PTR([pm](T &c, const MemberT val) { c.*pm = val; })); + return *this; + } + + // Access to the module + Package &package() { return p_package; } + + private: + template + constexpr void check_member_and_append_its_slots(const std::string &name, + MemberT CT::*) { + static_assert(std::is_base_of::value, + "member() requires a class member (or base class member)"); + auto &curr_class = p_package.p_classes_meta_data.back(); + + curr_class.slot_types = detail::str_append( + curr_class.slot_types, std::string(LispType() + "+").c_str()); + curr_class.slot_names = detail::str_append(curr_class.slot_names, + std::string(name + "+").c_str()); + } + + /// Define a member function + template + void defmethod(const std::string &name, R (CT::*f)(ArgsT...), + void (*func_ptr)()) { + auto curr_class = p_package.p_classes_meta_data.back(); + p_package.defun( + name, [f](T &obj, ArgsT... args) -> R { return (obj.*f)(args...); }, + true, curr_class.name, func_ptr); + } + + /// Define a member function, const version + template + void defmethod(const std::string &name, R (CT::*f)(ArgsT...) const, + void (*func_ptr)()) { + auto curr_class = p_package.p_classes_meta_data.back(); + p_package.defun( + name, + [f](const T &obj, ArgsT... args) -> R { return (obj.*f)(args...); }, + true, curr_class.name, func_ptr); + } + + /// Define a "member" function using a lambda + template + void defmethod( + const std::string &name, LambdaT &&lambda, void (*func_ptr)(), + typename std::enable_if::value, + bool>::type = true) { + auto curr_class = p_package.p_classes_meta_data.back(); + p_package.defun(name, std::forward(lambda), true, curr_class.name, + func_ptr); + } + + Package &p_package; +}; + +template +inline std::string general_class_name() { + auto classes = registry().current_package().general_classes(); + auto iter = classes.find(Hash32TypeName()); + return iter == classes.end() ? "" : iter->second; +} + +template +inline std::string pod_class_name() { + auto classes = registry().current_package().pod_classes(); + auto iter = classes.find(Hash32TypeName()); + return iter == classes.end() ? "" : iter->second; +} + +} // namespace clcxx +extern "C" { +CLCXX_API bool clcxx_init(void (*error_handler)(char *), + void (*reg_data_callback)(clcxx::MetaData *, + uint8_t)); +CLCXX_API bool remove_package(const char *pack_name); +CLCXX_API bool register_package(const char *cl_pack, + void (*regfunc)(clcxx::Package &)); +CLCXX_API size_t used_bytes_size(); +CLCXX_API size_t max_stack_bytes_size(); +CLCXX_API bool delete_string(char *string); +} + +#define CLCXX_PACKAGE extern "C" CLCXX_ONLY_EXPORTS void diff --git a/example/custom-lib/include/clcxx/type_conversion.hpp b/example/custom-lib/include/clcxx/type_conversion.hpp new file mode 100755 index 0000000..e6abef1 --- /dev/null +++ b/example/custom-lib/include/clcxx/type_conversion.hpp @@ -0,0 +1,617 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "clcxx_config.hpp" +#include "hash_type.hpp" +#include "memory.hpp" + +namespace clcxx { +// supported types are Primitive CFFI Types with +// void + complex + struct + class +// POD structs are passed through libffi +// structs,class are passed as pointers with *new* +// (e.g. (:pointer :char)) +// (:array :int 10) +// for nonPOD struct, class, pointer +// references are converted to pointers + +extern "C" typedef union { + float Float; + double Double; + // long double LongDouble; +} ComplexType; + +extern "C" typedef struct { + ComplexType real; + ComplexType imag; +} LispComplex; + +template +inline std::string general_class_name(); +template +inline std::string pod_class_name(); + +namespace internal { +template +struct is_complex { + static constexpr bool value = std::is_same_v> || + std::is_same_v>; +}; + +template +inline constexpr bool is_complex_v = is_complex::value; + +template +struct is_pod_struct { + static constexpr bool value = std::is_trivial_v && + std::is_standard_layout_v && + std::is_class_v && !is_complex_v; +}; + +template +inline constexpr bool is_pod_struct_v = is_pod_struct::value; + +template +struct is_std_string { + static constexpr bool value = + std::is_same_v, std::string>; +}; + +template +inline constexpr bool is_std_string_v = is_std_string::value; + +template +struct is_general_class { + static constexpr bool value = !(is_std_string_v || is_complex_v || + is_pod_struct_v)&&std::is_class_v; +}; + +template +inline constexpr bool is_general_class_v = is_general_class::value; + +namespace detail { +template +struct unused_type {}; + +template +struct DefineIfDifferent { + typedef T1 type; +}; + +template +struct DefineIfDifferent { + typedef unused_type type; +}; + +template +using define_if_different = typename DefineIfDifferent::type; + +} // namespace detail + +/// Convenience function to get the lisp data type associated with T +template +struct static_type_mapping { + typedef typename std::conditional_t, T, void *> type; + static std::string lisp_type() { + static_assert(std::is_class_v, "Unkown type"); + using ClassT = std::remove_cv_t; + if constexpr (is_pod_struct_v) + return std::string("(:struct " + pod_class_name() + ")"); + else + return std::string("(:class " + general_class_name() + ")"); + } +}; + +template <> +struct static_type_mapping { + typedef char type; + static std::string lisp_type() { return ":char"; } +}; +template <> +struct static_type_mapping< + detail::define_if_different> { + typedef unsigned char type; + static std::string lisp_type() { return ":uchar"; } +}; +template <> +struct static_type_mapping> { + typedef short type; + static std::string lisp_type() { return ":short"; } +}; +template <> +struct static_type_mapping< + detail::define_if_different> { + typedef unsigned short type; + static std::string lisp_type() { return ":ushort"; } +}; +template <> +struct static_type_mapping> { + typedef int type; + static std::string lisp_type() { return ":int"; } +}; +template <> +struct static_type_mapping< + detail::define_if_different> { + typedef unsigned int type; + static std::string lisp_type() { return ":uint"; } +}; +template <> +struct static_type_mapping> { + typedef long type; + static std::string lisp_type() { return ":long"; } +}; +template <> +struct static_type_mapping< + detail::define_if_different> { + typedef unsigned long type; + static std::string lisp_type() { return ":ulong"; } +}; +template <> +struct static_type_mapping> { + typedef long long type; + static std::string lisp_type() { return ":llong"; } +}; +template <> +struct static_type_mapping< + detail::define_if_different> { + typedef unsigned long long type; + static std::string lisp_type() { return ":ullong"; } +}; +template <> +struct static_type_mapping { + typedef int8_t type; + static std::string lisp_type() { return ":int8"; } +}; +template <> +struct static_type_mapping { + typedef uint8_t type; + static std::string lisp_type() { return ":uint8"; } +}; +template <> +struct static_type_mapping { + typedef int16_t type; + static std::string lisp_type() { return ":int16"; } +}; +template <> +struct static_type_mapping { + typedef uint16_t type; + static std::string lisp_type() { return ":uint16"; } +}; +template <> +struct static_type_mapping { + typedef int32_t type; + static std::string lisp_type() { return ":int32"; } +}; +template <> +struct static_type_mapping { + typedef uint32_t type; + static std::string lisp_type() { return ":uint32"; } +}; +template <> +struct static_type_mapping { + typedef int64_t type; + static std::string lisp_type() { return ":int64"; } +}; +template <> +struct static_type_mapping { + typedef uint64_t type; + static std::string lisp_type() { return ":uint64"; } +}; +template <> +struct static_type_mapping { + typedef float type; + static std::string lisp_type() { return ":float"; } +}; +template <> +struct static_type_mapping { + typedef double type; + static std::string lisp_type() { return ":double"; } +}; +// template <> struct static_type_mapping { +// typedef long double type; +// static std::string lisp_type() { return ":long-double"; } +// }; + +// References +template +struct static_type_mapping { + // reference to any type => passed and returned as pointers + typedef void *type; + static std::string lisp_type() { + return std::string("(:reference " + static_type_mapping::lisp_type() + + ")"); + } +}; + +template +struct static_type_mapping { + // reference to any type => passed and returned as pointers + typedef const void *type; + static std::string lisp_type() { + return std::string("(:const-reference " + + static_type_mapping::lisp_type() + ")"); + } +}; + +// resolve const types +template +struct static_type_mapping { + using l_type = typename static_type_mapping::type; + typedef l_type const type; + static std::string lisp_type() { return static_type_mapping::lisp_type(); } +}; + +template +struct static_type_mapping { + typedef void *type; + static std::string lisp_type() { + return std::string("(:pointer " + static_type_mapping::lisp_type() + + ")"); + } +}; + +template +struct static_type_mapping { + typedef T type[N]; + static std::string lisp_type() { + return std::string("(:array " + static_type_mapping::lisp_type() + " " + + N + ")"); + } +}; +template +struct static_type_mapping { + typedef T type[N]; + static std::string lisp_type() { + return std::string("(:array " + static_type_mapping::lisp_type() + " " + + N + ")"); + } +}; + +template <> +struct static_type_mapping { + typedef bool type; + static std::string lisp_type() { return ":bool"; } +}; +template <> +struct static_type_mapping { + typedef const char *type; + static std::string lisp_type() { return ":string+ptr"; } +}; +template <> +struct static_type_mapping { + typedef const char *type; + static std::string lisp_type() { return ":string+ptr"; } +}; +template <> +struct static_type_mapping { + typedef void type; + static std::string lisp_type() { return ":void"; } +}; + +template <> +struct static_type_mapping> { + typedef LispComplex type; + static std::string lisp_type() { + return std::string(std::string("(:complex ") + + static_type_mapping::lisp_type() + ")"); + } +}; + +template <> +struct static_type_mapping> { + typedef LispComplex type; + static std::string lisp_type() { + return std::string(std::string("(:complex ") + + static_type_mapping::lisp_type() + ")"); + } +}; + +// ------------------------------------------------------------------// +// Box an automatically converted value +/// Wrap a C++ pointer in void pointer lisp cffi +template +inline LispT box(CppT cpp_val) { + static_assert(std::is_pointer_v, "Box unkown type"); + return static_cast(cpp_val); +} + +template <> +inline const char *box(const char *str) { + const auto n = std::strlen(str); + auto new_str = static_cast( + MemPool().allocate((n + 1) * sizeof(char), std::alignment_of_v)); + std::strcpy(new_str, str); + new_str[n] = '\0'; + return new_str; +} + +template <> +inline LispComplex box(std::complex x) { + LispComplex c; + c.real.Float = std::real(x); + c.imag.Float = std::imag(x); + return c; +} + +template <> +inline LispComplex box(std::complex x) { + LispComplex c; + c.real.Double = std::real(x); + c.imag.Double = std::imag(x); + return c; +} + +// template <> inline LispComplex box(std::complex x) { +// LispComplex c; +// c.real.LongDouble = std::real(x); +// c.imag.LongDouble = std::imag(x); +// return c; +// } + +// unbox -----------------------------------------------------------------// +/// pointers +template +CppT unbox(LispT lisp_val) { + static_assert(std::is_pointer_v, "Box unkown type"); + return static_cast(lisp_val); +} + +template <> +inline const char *unbox(const char *v) { + return v; +} + +template <> +inline std::complex unbox(LispComplex v) { + return std::complex(v.real.Float, v.imag.Float); +} + +template <> +inline std::complex unbox(LispComplex v) { + return std::complex(v.real.Double, v.imag.Double); +} + +// template <> inline std::complex unbox(LispComplex v) { +// return std::complex(v.real.LongDouble, v.imag.LongDouble); +// } + +///////////////////////////////////// + +// Base template for converting to CPP +template +struct ConvertToCpp { + using LispT = void; + template + inline CppT *operator()(T &&) { + static_assert(sizeof(CppT) == 0, + "No appropriate specialization for ConvertToCpp"); + return nullptr; + } +}; + +// Fundamental/array/pod types conversion +template +struct ConvertToCpp || + std::is_array_v || is_pod_struct_v>> { + using LispT = typename static_type_mapping::type; + inline CppT operator()(LispT lisp_val) const { + static_assert(std::is_same_v, "Fundamental type mismatch"); + return lisp_val; + } +}; + +namespace detail { +template +struct RefToCpp { + // reference to pointer + CppT operator()(LispT lisp_val) const { + auto obj_ptr = + static_cast *>(lisp_val); + return *obj_ptr; + } +}; +} // namespace detail + +// reference conversion +template +struct ConvertToCpp>> { + using LispT = typename static_type_mapping::type; + CppT operator()(LispT lisp_val) const { + static_assert( + std::is_same_v || std::is_same_v, + "type mismatch"); + return detail::RefToCpp()(lisp_val); + } +}; + +// pointers conversion +template +struct ConvertToCpp>> { + using LispT = typename static_type_mapping::type; + CppT operator()(LispT lisp_val) const { return unbox(lisp_val); } +}; + +// complex numbers types +template +struct ConvertToCpp>> { + using LispT = typename static_type_mapping::type; + CppT operator()(LispT lisp_val) const { return unbox(lisp_val); } +}; + +// strings +template +struct ConvertToCpp>> { + using LispT = typename static_type_mapping::type; + std::string operator()(const char *str) const { + static_assert(std::is_same_v, "type mismatch"); + return std::string(ConvertToCpp()(str)); + } +}; + +// class +template +struct ConvertToCpp>> { + using LispT = typename static_type_mapping::type; + // return reference to avoid extra copy + CppT &operator()(LispT class_ptr) const { + static_assert(std::is_same_v, void *>, + "type mismatch"); + auto cpp_class_ptr = static_cast(class_ptr); + return *cpp_class_ptr; + } +}; + +// Base template for converting To lisp +template +struct ConvertToLisp { + using type = typename static_type_mapping::type; + template + LispT *operator()(CppT &&) { + static_assert(sizeof(CppT) == 0, + "No appropriate specialization for ConvertToLisp"); + return nullptr; + } +}; + +template +struct ConvertToLisp>> { + using type = typename static_type_mapping::type; + template + void operator()() { + static_assert(sizeof(CppT) == 0, + "No appropriate specialization for ConvertToLisp"); + } +}; + +// Fundamental type conversion +template +struct ConvertToLisp && + (std::is_fundamental_v || + std::is_array_v || + is_pod_struct_v)>> { + using type = typename static_type_mapping::type; + CppT operator()(CppT cpp_val) const { + static_assert(std::is_same_v, "type mismatch"); + return cpp_val; + } +}; + +namespace detail { +template +struct RefToLisp { + LispT operator()(CppT cpp_val) const { + return static_cast(std::addressof(cpp_val)); + } +}; +} // namespace detail + +// Reference conversion +template +struct ConvertToLisp>> { + // reference to fundamental type + using type = typename static_type_mapping::type; + using LispT = typename static_type_mapping::type; + LispT operator()(CppT cpp_val) const { + static_assert( + std::is_same_v || std::is_same_v, + "type mismatch"); + return detail::RefToLisp()(cpp_val); + } +}; + +// pointers conversion +template +struct ConvertToLisp>> { + using type = typename static_type_mapping::type; + using LispT = typename static_type_mapping::type; + LispT operator()(CppT cpp_val) const { + static_assert(std::is_pointer_v, "type mismatch"); + return box(cpp_val); + } +}; + +// complex numbers types +template +struct ConvertToLisp>> { + using type = typename static_type_mapping::type; + using LispT = typename static_type_mapping::type; + LispT operator()(CppT cpp_val) const { + static_assert(std::is_same_v, "type mismatch"); + return box(cpp_val); + } +}; + +// Srings +template +struct ConvertToLisp>> { + using type = typename static_type_mapping::type; + const char *operator()(const std::string &str) const { + static_assert(std::is_same_v, "type mismatch"); + return ConvertToLisp()(str.c_str()); + } +}; + +// class exclude std::string +template +struct ConvertToLisp>> { + using type = typename static_type_mapping::type; + using LispT = typename static_type_mapping::type; + LispT operator()(CppT cpp_class) const { + static_assert(std::is_same_v, void *>, + "type mismatch"); + + auto obj_ptr = static_cast( + MemPool().allocate(sizeof(CppT), std::alignment_of_v)); + ::new (obj_ptr) CppT(std::move(cpp_class)); + return static_cast(obj_ptr); + } +}; + +template +struct CppTypeAdapter { + using type = T; +}; +template +struct CppTypeAdapter< + T, typename std::enable_if_t>> { + using type = T &; +}; + +} // namespace internal + +template +inline std::string LispType() { + return internal::static_type_mapping::lisp_type(); +} + +///// +template +using ToLisp_t = typename internal::ConvertToLisp::type; + +template +using ToCpp_t = typename internal::CppTypeAdapter::type; + +/// Conversion to lisp +template +inline ToLisp_t ToLisp(CppT cpp_val) { + return internal::ConvertToLisp()(std::forward(cpp_val)); +} + +/// Conversion to C++ +template +inline ToCpp_t ToCpp(LispT &&lisp_val) { + return internal::ConvertToCpp()(std::forward(lisp_val)); +} + +} // namespace clcxx diff --git a/example/custom-lib/src/my-lib.cpp b/example/custom-lib/src/my-lib.cpp new file mode 100644 index 0000000..37eb3a4 --- /dev/null +++ b/example/custom-lib/src/my-lib.cpp @@ -0,0 +1,80 @@ +#include +#include "clcxx/clcxx.hpp" + +// standard C function definitions: + +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 x) { return x; } +std::string hi(char* s) { return std::string("hi, " + std::string(s)); } +void ref_int(int& x) { x += 30; } + +// C++ class definition with members and method: class xx { public: + +class xx { + public: + xx(int xx, int yy) : y(yy), x(xx) {} + std::string greet() { return "Hello, World"; } + int y; + int x; +}; +void ref_class(xx& x) { x.y = 1000000; } + +// definitions of the API exposure to Common Lisp: +// +// CLCXX_PACKAGE defines a Common Lisp package which can be loaded by +// Common Lisp with the command "(add-package "TEST" "CL-TEST")" +// +// The first argument of add-package is the name of the Package +// defined with CLCXX_PACKAGE in the cpp file. +// +// The second argument of add-package is the name of the common-lisp +// package to define. +// +// pack is the variable name referencing the package. +// +// The methods are: +// +// .defun (std::string "lisp-fn-name", fn-pointer c-fn) +// +// defines the Common Lisp function #'cl-test:lisp-fn-name to call +// "c-fn" from the code above. +// +// .defclass ("lisp-class-name") +// +// defines a class accessor for Common Lisp +// +// .member("cl-name", class-pointer c-name) +// +// define a member of the class +// +// .defmethod("cl-name", fn-pointer c-method) +// +// the class's setter and getter functions are also defined this way: +// +// .defmethod("foo.x", F_PTR([](xx x){return x.x;})) +// +// defines the methods #'cl-test:foo.x as getter and +// #'cl-test:foo.x.set as setter for the member x of class xx. +// +// .constructor defines a constructor function for Common-lisp +// +// .constructor("create-xx"); defines a Common Lisp +// constructor function #'cl-test:create-xx with two integer +// arguments. + +CLCXX_PACKAGE TEST(clcxx::Package& pack) { + pack.defun("hi", F_PTR(&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") + .member("y", &xx::y) + .defmethod("foo", F_PTR(&xx::greet)) + .defmethod("foo.x", F_PTR([](xx x){return x.x;})) + .constructor("create-xx"); +} diff --git a/example/my-cl-lib.asd b/example/my-cl-lib.asd new file mode 100644 index 0000000..2c98610 --- /dev/null +++ b/example/my-cl-lib.asd @@ -0,0 +1,14 @@ +;;;; cl-link.asd +;; +;;;; Copyright (c) 2023 Orm Finnendahl + +(asdf:defsystem #:cl-link + :description "Describe cl-link here" + :author "Orm Finnendahl " + :license "gpl 2.0 or later" + :version "0.0.1" + :serial t + :depends-on (:cffi :cxx) + :components ((:file "package") + (:file "load-lib") + (:file "export-syms"))) diff --git a/example/package.lisp b/example/package.lisp new file mode 100644 index 0000000..e5b8f76 --- /dev/null +++ b/example/package.lisp @@ -0,0 +1,7 @@ +;;;; package.lisp +;; +;;;; Copyright (c) 2023 Orm Finnendahl + +(defpackage #:cl-link + (:use #:cl) + ) From 3746f7578ace8a471bd7770cc00b08d1e88c56a4 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 10:19:25 +0100 Subject: [PATCH 30/46] misc. --- doc/README.org | 116 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 41 deletions(-) diff --git a/doc/README.org b/doc/README.org index f322a3b..633f437 100644 --- a/doc/README.org +++ b/doc/README.org @@ -119,47 +119,6 @@ class xx { void ref_class(xx& x) { x.y = 1000000; } // definitions of the API exposure to Common Lisp: -// -// CLCXX_PACKAGE defines a Common Lisp package which can be loaded by -// Common Lisp with the command "(add-package "TEST" "CL-TEST")" -// -// The first argument of add-package is the name of the Package -// defined with CLCXX_PACKAGE in the cpp file. -// -// The second argument of add-package is the name of the common-lisp -// package to define. -// -// pack is the variable name referencing the package. -// -// The methods are: -// -// .defun (std::string "lisp-fn-name", fn-pointer c-fn) -// -// defines the Common Lisp function #'cl-test:lisp-fn-name to call -// "c-fn" from the code above. -// -// .defclass ("lisp-class-name") -// -// defines a class accessor for Common Lisp -// -// .member("cl-name", class-pointer c-name) -// -// define a member of the class -// -// .defmethod("cl-name", fn-pointer c-method) -// -// the class's setter and getter functions are also defined this way: -// -// .defmethod("foo.x", F_PTR([](xx x){return x.x;})) -// -// defines the methods #'cl-test:foo.x as getter and -// #'cl-test:foo.x.set as setter for the member x of class xx. -// -// .constructor defines a constructor function for Common-lisp -// -// .constructor("create-xx"); defines a Common Lisp -// constructor function #'cl-test:create-xx with two integer -// arguments. CLCXX_PACKAGE TEST(clcxx::Package& pack) { pack.defun("hi", F_PTR(&hi)); @@ -178,6 +137,74 @@ CLCXX_PACKAGE TEST(clcxx::Package& pack) { #+END_SRC + +*** Definitions of the API exposure to Common Lisp + + The API exposure to Common Lisp happens inside a =CLCXX_PACKAGE= + block of the C++ file. After compilation of the lib and its + loading from Common Lisp, all the definitions get pulled into a + custom, newly created Common Lisp package with the command + =add-package=. The command takes two arguments: The name defined + in the C++ file ("TEST" in the example) and the name of the Common + Lisp package to use ("CL-TEST" in the example). + + The =CLCXX_PACKAGE= block defines the bindings of the C++ + functions, classes, methods, members and constructors to Common + lisp functions. Everything *not* defined here will not be visible + by Common Lisp. + +#+BEGIN_SRC c +CLCXX_PACKAGE TEST(clcxx::Package& pack) { +// ... +} +#+END_SRC + +Defines the package named =TEST=, using the package pointer =pack=. + +#+BEGIN_SRC c + pack.defun("greet", F_PTR(&greet)); +#+END_SRC + +Defines the Common Lisp function =#'cl-test:greet= to call the C++ +function =greet=. + +#+BEGIN_SRC c + pack.defclass("xx") +#+END_SRC + +Defines a class framework of the C++ class =xx= for Common Lisp. + +#+BEGIN_SRC c + .member("y", &xx::y) +#+END_SRC + +Define the member =#'cl-test:y= + +#+BEGIN_SRC c +.defmethod("cl-name", fn-pointer c-method) +#+END_SRC + +the class's setter and getter functions are also defined this way: + +#+BEGIN_SRC c +.defmethod("foo.x", F_PTR([](xx x){return x.x;})) +#+END_SRC + +defines the methods #'cl-test:foo.x as getter and +#'cl-test:foo.x.set as setter for the member x of class xx. + +.constructor defines a constructor function for Common-lisp + +#+BEGIN_SRC c +.constructor("create-xx") +#+END_SRC + +defines a Common Lisp +constructor function #'cl-test:create-xx with two integer +arguments. + + + ** Building the Library #+BEGIN_SRC bash @@ -205,3 +232,10 @@ Examples - 2 or 3 end to end examples, with extensive documentation please send bug reports, code to custom libs using the system, etc. for expanding the user base. + + - The first argument of add-package is the name of the Package + defined with =CLCXX​_PACKAGE= in the cpp file. + +- The second argument of add-package is the name of the common-lisp + package to define. + From 65124aa60cc18e89b8fb67be1d67bd2407dad2d7 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 10:21:44 +0100 Subject: [PATCH 31/46] misc --- doc/README.org | 6 ++-- example/custom-lib/src/my-lib.cpp | 47 ++----------------------------- 2 files changed, 6 insertions(+), 47 deletions(-) diff --git a/doc/README.org b/doc/README.org index 633f437..b8ce6dd 100644 --- a/doc/README.org +++ b/doc/README.org @@ -98,7 +98,7 @@ header files into the =custom-lib/include= folder or optionally into #include #include "clcxx/clcxx.hpp" -// standard C function definitions: +// standard C function definitions std::string greet() { return "Hello, World"; } int Int(int x) { return x + 100; } @@ -107,7 +107,7 @@ auto gr(std::complex x) { return x; } std::string hi(char* s) { return std::string("hi, " + std::string(s)); } void ref_int(int& x) { x += 30; } -// C++ class definition with members and method: class xx { public: +// standard C++ class definition with members and method class xx { public: @@ -118,7 +118,7 @@ class xx { }; void ref_class(xx& x) { x.y = 1000000; } -// definitions of the API exposure to Common Lisp: +// definitions of the API exposure to Common Lisp CLCXX_PACKAGE TEST(clcxx::Package& pack) { pack.defun("hi", F_PTR(&hi)); diff --git a/example/custom-lib/src/my-lib.cpp b/example/custom-lib/src/my-lib.cpp index 37eb3a4..723060e 100644 --- a/example/custom-lib/src/my-lib.cpp +++ b/example/custom-lib/src/my-lib.cpp @@ -1,7 +1,7 @@ #include #include "clcxx/clcxx.hpp" -// standard C function definitions: +// standard C function definitions std::string greet() { return "Hello, World"; } int Int(int x) { return x + 100; } @@ -10,7 +10,7 @@ auto gr(std::complex x) { return x; } std::string hi(char* s) { return std::string("hi, " + std::string(s)); } void ref_int(int& x) { x += 30; } -// C++ class definition with members and method: class xx { public: +// standard C++ class definition with members and method class xx { public: @@ -21,48 +21,7 @@ class xx { }; void ref_class(xx& x) { x.y = 1000000; } -// definitions of the API exposure to Common Lisp: -// -// CLCXX_PACKAGE defines a Common Lisp package which can be loaded by -// Common Lisp with the command "(add-package "TEST" "CL-TEST")" -// -// The first argument of add-package is the name of the Package -// defined with CLCXX_PACKAGE in the cpp file. -// -// The second argument of add-package is the name of the common-lisp -// package to define. -// -// pack is the variable name referencing the package. -// -// The methods are: -// -// .defun (std::string "lisp-fn-name", fn-pointer c-fn) -// -// defines the Common Lisp function #'cl-test:lisp-fn-name to call -// "c-fn" from the code above. -// -// .defclass ("lisp-class-name") -// -// defines a class accessor for Common Lisp -// -// .member("cl-name", class-pointer c-name) -// -// define a member of the class -// -// .defmethod("cl-name", fn-pointer c-method) -// -// the class's setter and getter functions are also defined this way: -// -// .defmethod("foo.x", F_PTR([](xx x){return x.x;})) -// -// defines the methods #'cl-test:foo.x as getter and -// #'cl-test:foo.x.set as setter for the member x of class xx. -// -// .constructor defines a constructor function for Common-lisp -// -// .constructor("create-xx"); defines a Common Lisp -// constructor function #'cl-test:create-xx with two integer -// arguments. +// definitions of the API exposure to Common Lisp CLCXX_PACKAGE TEST(clcxx::Package& pack) { pack.defun("hi", F_PTR(&hi)); From 6efe620bdcd24cdac6e0db0272edd305d2e60d93 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 10:23:11 +0100 Subject: [PATCH 32/46] misc --- doc/README.org | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/README.org b/doc/README.org index b8ce6dd..d3d54bb 100644 --- a/doc/README.org +++ b/doc/README.org @@ -145,8 +145,9 @@ CLCXX_PACKAGE TEST(clcxx::Package& pack) { loading from Common Lisp, all the definitions get pulled into a custom, newly created Common Lisp package with the command =add-package=. The command takes two arguments: The name defined - in the C++ file ("TEST" in the example) and the name of the Common - Lisp package to use ("CL-TEST" in the example). + in the C++ file ("TEST" in the example above) and the name of the + Common Lisp package to use ("CL-TEST" in the Common Lisp example + below). The =CLCXX_PACKAGE= block defines the bindings of the C++ functions, classes, methods, members and constructors to Common From 13cdf91c04e976414702e48215dd0d56119d56bf Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 10:57:37 +0100 Subject: [PATCH 33/46] added example code. --- example/export-syms.lisp | 41 ++++++++++++++++++++++++++++++++++++++++ example/load-lib.lisp | 29 ++++++++++++++++++++++++++++ example/my-cl-lib.asd | 9 ++++----- example/package.lisp | 2 +- example/scratch.lisp | 23 ++++++++++++++++++++++ package.lisp | 0 6 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 example/export-syms.lisp create mode 100644 example/load-lib.lisp mode change 100644 => 100755 example/package.lisp create mode 100644 example/scratch.lisp mode change 100755 => 100644 package.lisp diff --git a/example/export-syms.lisp b/example/export-syms.lisp new file mode 100644 index 0000000..abf4a29 --- /dev/null +++ b/example/export-syms.lisp @@ -0,0 +1,41 @@ +;;; +;;; export-syms.lisp +;;; +;;; ********************************************************************** +;;; Copyright (c) 2023 Orm Finnendahl +;;; +;;; Revision history: See git repository. +;;; +;;; This program is free software; you can redistribute it and/or +;;; modify it under the terms of the Gnu Public License, version 2 or +;;; later. See https://www.gnu.org/licenses/gpl-2.0.html for the text +;;; of this agreement. +;;; +;;; This program is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; ********************************************************************** + +(in-package :my-cl-lib) + +;;; optional exports from my-cl-lib to make the symbols of cl-test +;;; available. For this to work you have to uncomment (use-package +;;; "cl-test") in load-lisp.lib + +(export + '(y.set + create-xx + cxx-ptr + destruct-xx + foo + foo.x + greet + hi + ref-class + ref-int + test-complex + test-float + test-int + y.get)) diff --git a/example/load-lib.lisp b/example/load-lib.lisp new file mode 100644 index 0000000..61126cb --- /dev/null +++ b/example/load-lib.lisp @@ -0,0 +1,29 @@ +;;;; load-lib.lisp + +(in-package :my-cl-lib) + +;;; change this to the load path of libClCxx +(pushnew (pathname "/usr/local/lib/") + cffi:*foreign-library-directories* + :test #'equal) + +(cffi:define-foreign-library clcxx + (t (:default "libClCxx"))) + +(pushnew (asdf:system-relative-pathname :my-cl-lib "custom-lib/lib/") + cffi:*foreign-library-directories* + :test #'equal) + +(cffi:define-foreign-library my-lib + (t (:default "libMyLib"))) + +(cffi:use-foreign-library clcxx) +(cffi:use-foreign-library my-lib) + +(cxx:init) + +(cxx:add-package "CL-TEST" "TEST") + +;;; optionally import the symbols of cl-test into my-cl-lib +;;; (use-package :cl-test) + diff --git a/example/my-cl-lib.asd b/example/my-cl-lib.asd index 2c98610..567d68b 100644 --- a/example/my-cl-lib.asd +++ b/example/my-cl-lib.asd @@ -1,9 +1,7 @@ ;;;; cl-link.asd -;; -;;;; Copyright (c) 2023 Orm Finnendahl -(asdf:defsystem #:cl-link - :description "Describe cl-link here" +(asdf:defsystem #:my-cl-lib + :description "example for cl-cxx" :author "Orm Finnendahl " :license "gpl 2.0 or later" :version "0.0.1" @@ -11,4 +9,5 @@ :depends-on (:cffi :cxx) :components ((:file "package") (:file "load-lib") - (:file "export-syms"))) +;;; (:file "export-syms") + )) diff --git a/example/package.lisp b/example/package.lisp old mode 100644 new mode 100755 index e5b8f76..586b2db --- a/example/package.lisp +++ b/example/package.lisp @@ -2,6 +2,6 @@ ;; ;;;; Copyright (c) 2023 Orm Finnendahl -(defpackage #:cl-link +(defpackage #:my-cl-lib (:use #:cl) ) diff --git a/example/scratch.lisp b/example/scratch.lisp new file mode 100644 index 0000000..a408082 --- /dev/null +++ b/example/scratch.lisp @@ -0,0 +1,23 @@ +;;; +;;; scratch.lisp +;;; + +(in-package :my-cl-lib) + +(cl-test:greet) -> "Hello, World" + +(defparameter *testclass* (cl-test:create-xx 10 20)) + +(cl-test:foo *testclass*) + +(cl-test:foo.x *testclass*) + +(cl-test:y.get *testclass*) + +(cl-test:y.set *testclass* 22) + +;;; avoiding the leading cl-test: + +(use-package :cl-test) + +(greet) diff --git a/package.lisp b/package.lisp old mode 100755 new mode 100644 From ae9eb456d105c36aa83ad29db4517e2e4c2f2917 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 11:23:58 +0100 Subject: [PATCH 34/46] misc README --- doc/README.org | 45 ++++++++++++++++++++----------- example/custom-lib/src/my-lib.cpp | 5 ++-- example/scratch.lisp | 1 + 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/doc/README.org b/doc/README.org index d3d54bb..cb5d627 100644 --- a/doc/README.org +++ b/doc/README.org @@ -128,11 +128,11 @@ CLCXX_PACKAGE TEST(clcxx::Package& pack) { 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") + pack.defclass("cl-xx") .member("y", &xx::y) .defmethod("foo", F_PTR(&xx::greet)) .defmethod("foo.x", F_PTR([](xx x){return x.x;})) - .constructor("create-xx"); + .constructor(); } #+END_SRC @@ -170,41 +170,56 @@ Defines the Common Lisp function =#'cl-test:greet= to call the C++ function =greet=. #+BEGIN_SRC c - pack.defclass("xx") + pack.defclass("cl-xx") #+END_SRC -Defines a class framework of the C++ class =xx= for Common Lisp. +Defines a class framework of the C​++ class =xx= for Common Lisp (named +="cl-xx"= in Common Lisp). This creates bindings for the Common Lisp +destructor function =#'cl-test:destruct-cl-xx= #+BEGIN_SRC c .member("y", &xx::y) #+END_SRC -Define the member =#'cl-test:y= +Define the member =#'cl-test:y= of the xx class. This creates +bindinges for the Common Lisp getter and setter functions +=#'cl-test:y.get= and =#'cl-test:y.set=. #+BEGIN_SRC c -.defmethod("cl-name", fn-pointer c-method) + .defmethod("foo", F_PTR(&xx::greet)) #+END_SRC -the class's setter and getter functions are also defined this way: +Defines the Common Lisp function =#'cl-test:foo= as the greet method +of class xx. The Common Lisp functions binding methods will always +take the instance of the class as first argument and the arguments of +the C method as additional arguments (if any). #+BEGIN_SRC c .defmethod("foo.x", F_PTR([](xx x){return x.x;})) #+END_SRC -defines the methods #'cl-test:foo.x as getter and -#'cl-test:foo.x.set as setter for the member x of class xx. - -.constructor defines a constructor function for Common-lisp +An alternative way to define a getter function of the member x of +class xx using a C​++ lambda expression. #+BEGIN_SRC c -.constructor("create-xx") +.constructor() #+END_SRC -defines a Common Lisp -constructor function #'cl-test:create-xx with two integer -arguments. +Define a constructor function for the class =xx=. The constructor +function is automatically named =cl-test:create-cl-xx2=. The number at +the end of the name specifies the number of arguments of the +constructor function and is omitted when the constructor function +takes no arguments. + +Alternatively you can specify a Common Lisp name of the constructor +function explicitely: +#+BEGIN_SRC c +.constructor("create-my-xx") +#+END_SRC +This will create the Common Lisp Constructor function +=#'cl-test:create-my-xx=. ** Building the Library diff --git a/example/custom-lib/src/my-lib.cpp b/example/custom-lib/src/my-lib.cpp index 723060e..887abaf 100644 --- a/example/custom-lib/src/my-lib.cpp +++ b/example/custom-lib/src/my-lib.cpp @@ -31,9 +31,10 @@ CLCXX_PACKAGE TEST(clcxx::Package& pack) { 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") + pack.defclass("cl-xx") .member("y", &xx::y) .defmethod("foo", F_PTR(&xx::greet)) .defmethod("foo.x", F_PTR([](xx x){return x.x;})) - .constructor("create-xx"); + .constructor() + ; } diff --git a/example/scratch.lisp b/example/scratch.lisp index a408082..9bce7f4 100644 --- a/example/scratch.lisp +++ b/example/scratch.lisp @@ -4,6 +4,7 @@ (in-package :my-cl-lib) +(cl-test: ) (cl-test:greet) -> "Hello, World" (defparameter *testclass* (cl-test:create-xx 10 20)) From bb8591aeffc8b86d4fc5aac9f77e915cf76f6d55 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 11:25:18 +0100 Subject: [PATCH 35/46] misc README --- doc/README.org | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/README.org b/doc/README.org index cb5d627..aa335e2 100644 --- a/doc/README.org +++ b/doc/README.org @@ -178,7 +178,7 @@ Defines a class framework of the C​++ class =xx= for Common Lisp (named destructor function =#'cl-test:destruct-cl-xx= #+BEGIN_SRC c - .member("y", &xx::y) + pack.member("y", &xx::y) #+END_SRC Define the member =#'cl-test:y= of the xx class. This creates @@ -186,7 +186,7 @@ bindinges for the Common Lisp getter and setter functions =#'cl-test:y.get= and =#'cl-test:y.set=. #+BEGIN_SRC c - .defmethod("foo", F_PTR(&xx::greet)) + pack.defmethod("foo", F_PTR(&xx::greet)) #+END_SRC Defines the Common Lisp function =#'cl-test:foo= as the greet method @@ -195,14 +195,14 @@ take the instance of the class as first argument and the arguments of the C method as additional arguments (if any). #+BEGIN_SRC c -.defmethod("foo.x", F_PTR([](xx x){return x.x;})) + pack.#define efmethod("foo.x", F_PTR([](xx x){return x.x;})) #+END_SRC An alternative way to define a getter function of the member x of class xx using a C​++ lambda expression. #+BEGIN_SRC c -.constructor() + pack.constructor() #+END_SRC Define a constructor function for the class =xx=. The constructor @@ -215,7 +215,7 @@ Alternatively you can specify a Common Lisp name of the constructor function explicitely: #+BEGIN_SRC c -.constructor("create-my-xx") + pack.constructor("create-my-xx") #+END_SRC This will create the Common Lisp Constructor function From 87391031dfb38588b1e076e0ec4c117183c7688e Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 11:53:45 +0100 Subject: [PATCH 36/46] misc README --- doc/README.org | 95 +++++++++++++++++++++++++++++++++++++++++--- example/scratch.lisp | 20 ++++++++-- 2 files changed, 105 insertions(+), 10 deletions(-) diff --git a/doc/README.org b/doc/README.org index aa335e2..e505794 100644 --- a/doc/README.org +++ b/doc/README.org @@ -190,12 +190,13 @@ bindinges for the Common Lisp getter and setter functions #+END_SRC Defines the Common Lisp function =#'cl-test:foo= as the greet method -of class xx. The Common Lisp functions binding methods will always -take the instance of the class as first argument and the arguments of -the C method as additional arguments (if any). +of class xx. The Common Lisp functions binding C​++ class methods will +always take the instance of the class (meaning a CFFI foreign pointer +to it) as first argument and the arguments of the C method as +additional arguments (if any). #+BEGIN_SRC c - pack.#define efmethod("foo.x", F_PTR([](xx x){return x.x;})) + pack.defmethod("foo.x", F_PTR([](xx x){return x.x;})) #+END_SRC An alternative way to define a getter function of the member x of @@ -237,8 +238,90 @@ This should compile a shared library named =myLib.so= (maybe with another extension according to your OS) and put it into the folder =/tmp/example/my-lib/lib/= - Following is a full example -Examples - 2 or 3 end to end examples, with extensive documentation +** Using the Library from Common Lisp + + In order to use the Library from Common Lisp refer to the file + =/tmp/example/load-lib.lisp= + + The standard =CFFI= way of loading a lib is also used for + =cl-cxx=. Before loading the custom library, the CLCXX library has + to be loaded. We assume the library is located in + =/usr/local/lib=. Note that plain CLCXX from github will install it + in =~/.local/lib=. To install in /usr/local/lib instead, use =cmake + -DCMAKE_INSTALL_PREFIX=/usr/local ..= instead of =cmake ..= in the + build step of CLCXX. + + First we define both libs: + +#+BEGIN_SRC lisp + +;;; change this to the load path of libClCxx + (pushnew (pathname "/usr/local/lib/") + cffi:*foreign-library-directories* + :test #'equal) + + (cffi:define-foreign-library clcxx + (t (:default "libClCxx"))) + + (pushnew (asdf:system-relative-pathname :my-cl-lib "custom-lib/lib/") + cffi:*foreign-library-directories* + :test #'equal) + + (cffi:define-foreign-library my-lib + (t (:default "libMyLib"))) +#+END_SRC + + Then we load them into CFFI: + +#+BEGIN_SRC lisp + (cffi:use-foreign-library clcxx) + (cffi:use-foreign-library my-lib) +#+END_SRC + + After this, CLCXX needs to be initialized: +#+BEGIN_SRC lisp + (cxx:init) +#+END_SRC + + Finally we add all the bindings from C​++ to Common Lisp in the + newly created package "CL_TEST". This step is comparable to the + loading of a file containing =(defcfun ...)= and such in CFFI: + +#+BEGIN_SRC lisp + (cxx:add-package "CL-TEST" "TEST") +#+END_SRC + + After this step, all definitions of the C​++ file should be + available in Common Lisp. + +** Exploring the example code + + The example code for the Common Lisp part is also located in the + =/tmp/example/= directory. To load it into lisp, make the example + directory visible to asdf or quicklisp. In quicklisp this can be + done like this: + +#+BEGIN_SRC bash + +$ ln -s /tmp/example ~/quicklisp/local-projects + +#+END_SRC + + Then start up Common Lisp and load the project with: + +#+BEGIN_SRC lisp + +Cl-USER​> (ql:quickload "my-cl-lib") +To load "my-cl-lib": + Load 1 ASDF system: + my-cl-lib +; Loading "my-cl-lib" +............... +("my-cl-lib") +CL-USER​> + +#+END_SRC + * Future Direction diff --git a/example/scratch.lisp b/example/scratch.lisp index 9bce7f4..39c0761 100644 --- a/example/scratch.lisp +++ b/example/scratch.lisp @@ -4,21 +4,33 @@ (in-package :my-cl-lib) -(cl-test: ) -(cl-test:greet) -> "Hello, World" +(cl-test:greet) +;;; -> "Hello, World" -(defparameter *testclass* (cl-test:create-xx 10 20)) +(defparameter *testclass* (cl-test:create-cl-xx2 10 20)) +;;; -> *TESTCLASS* (cl-test:foo *testclass*) +;;; -> "Hello, World" (cl-test:foo.x *testclass*) +;;; -> 10 (cl-test:y.get *testclass*) +;;; -> 20 (cl-test:y.set *testclass* 22) +;;; ; No value -;;; avoiding the leading cl-test: + + +;;; import the symbols to avoid the leading cl-test: in the function +;;; calls (use-package :cl-test) (greet) +;;; -> "Hello, World" + + + From b834db07b2e252daa0ce025e68d560d2e81c13cf Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 11:54:57 +0100 Subject: [PATCH 37/46] misc --- doc/README.org | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README.org b/doc/README.org index e505794..7b50e2e 100644 --- a/doc/README.org +++ b/doc/README.org @@ -255,13 +255,13 @@ another extension according to your OS) and put it into the folder #+BEGIN_SRC lisp -;;; change this to the load path of libClCxx + ;;; change this to the load path of libClCxx (pushnew (pathname "/usr/local/lib/") cffi:*foreign-library-directories* :test #'equal) (cffi:define-foreign-library clcxx - (t (:default "libClCxx"))) + (t (:default "libClCxx"))) (pushnew (asdf:system-relative-pathname :my-cl-lib "custom-lib/lib/") cffi:*foreign-library-directories* From acc0998f951e671469e3809e14d843f665bfaca2 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 11:55:53 +0100 Subject: [PATCH 38/46] misc --- doc/README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.org b/doc/README.org index 7b50e2e..ac09a08 100644 --- a/doc/README.org +++ b/doc/README.org @@ -284,7 +284,7 @@ another extension according to your OS) and put it into the folder #+END_SRC Finally we add all the bindings from C​++ to Common Lisp in the - newly created package "CL_TEST". This step is comparable to the + newly created package "CL-TEST". This step is comparable to the loading of a file containing =(defcfun ...)= and such in CFFI: #+BEGIN_SRC lisp From eb5c73999c7aa471b75751586654e806ba9d3ece Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 12:03:53 +0100 Subject: [PATCH 39/46] misc README --- doc/README.org | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/doc/README.org b/doc/README.org index ac09a08..3750338 100644 --- a/doc/README.org +++ b/doc/README.org @@ -13,6 +13,26 @@ The code was inspired by Julia's [[https://github.com/JuliaInterop/CxxWrap.jl][CxxWrap]] and provides a similar functionality as python's [[https://github.com/pybind/pybind11][pybind11]] for the Common Lisp community. +* Quickstart + + - Install CLCXX into /usr/local/lib + + - copy the =example= directory to =/tmp/example= + + - with quicklisp installed do + +#+BEGIN_SRC bash + $ ln -s /tmp/example ~/quicklisp/local-projects +#+END_SRC + + - Start Common Lisp and issue + +#+BEGIN_SRC lisp + Cl-USER​> (ql:quickload "my-cl-lib") +#+END_SRC + + - Explore the example code in =/tmp/example/scratch.lisp= + * Architecture ** Provided parts @@ -298,8 +318,9 @@ another extension according to your OS) and put it into the folder The example code for the Common Lisp part is also located in the =/tmp/example/= directory. To load it into lisp, make the example - directory visible to asdf or quicklisp. In quicklisp this can be - done like this: + directory visible to asdf or quicklisp. + + In quicklisp this can be done like this: #+BEGIN_SRC bash @@ -321,7 +342,9 @@ To load "my-cl-lib": CL-USER​> #+END_SRC - + + Open the file =/tmp/example/scratch.lisp= to see and explore + commented examples of using the lib. * Future Direction From 3ac5d5b911dc7b2dcaa3ab3c8d1f74eade95691e Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 12:05:46 +0100 Subject: [PATCH 40/46] misc --- doc/README.org | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/doc/README.org b/doc/README.org index 3750338..23d36e8 100644 --- a/doc/README.org +++ b/doc/README.org @@ -96,10 +96,8 @@ - Copy the folder with all contents to a location of your choice: #+BEGIN_SRC bash - $ cp -R examples /tmp/ $ cd /tmp/examples - #+END_SRC The necessary files for the custom C++ library are located in the @@ -114,7 +112,6 @@ header files into the =custom-lib/include= folder or optionally into are its contents: #+BEGIN_SRC c - #include #include "clcxx/clcxx.hpp" @@ -154,7 +151,6 @@ CLCXX_PACKAGE TEST(clcxx::Package& pack) { .defmethod("foo.x", F_PTR([](xx x){return x.x;})) .constructor(); } - #+END_SRC @@ -245,13 +241,11 @@ This will create the Common Lisp Constructor function ** Building the Library #+BEGIN_SRC bash - $ cd /tmp/examples/my-lib $ mkdir build $ cd build $ cmake .. $ make - #+END_SRC This should compile a shared library named =myLib.so= (maybe with @@ -274,7 +268,6 @@ another extension according to your OS) and put it into the folder First we define both libs: #+BEGIN_SRC lisp - ;;; change this to the load path of libClCxx (pushnew (pathname "/usr/local/lib/") cffi:*foreign-library-directories* @@ -352,12 +345,6 @@ CL-USER​> * Help Wanted - please send bug reports, code to custom libs using the system, - etc. for expanding the user base. - - - The first argument of add-package is the name of the Package - defined with =CLCXX​_PACKAGE= in the cpp file. - -- The second argument of add-package is the name of the common-lisp - package to define. + please send bug reports, suggestions, comments and code to custom + libs using the system, etc. for expanding the user base. From 730770897d03f84d0c3e85a513d927707cf61478 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 12:09:16 +0100 Subject: [PATCH 41/46] misc --- doc/README.org | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/doc/README.org b/doc/README.org index 23d36e8..dbc7c1f 100644 --- a/doc/README.org +++ b/doc/README.org @@ -259,13 +259,13 @@ another extension according to your OS) and put it into the folder The standard =CFFI= way of loading a lib is also used for =cl-cxx=. Before loading the custom library, the CLCXX library has - to be loaded. We assume the library is located in + to be loaded. The lisp code assumes the library is located in =/usr/local/lib=. Note that plain CLCXX from github will install it in =~/.local/lib=. To install in /usr/local/lib instead, use =cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..= instead of =cmake ..= in the build step of CLCXX. - First we define both libs: + First both libs are defined: #+BEGIN_SRC lisp ;;; change this to the load path of libClCxx @@ -284,7 +284,7 @@ another extension according to your OS) and put it into the folder (t (:default "libMyLib"))) #+END_SRC - Then we load them into CFFI: + Then the libs are loaded into CFFI: #+BEGIN_SRC lisp (cffi:use-foreign-library clcxx) @@ -292,13 +292,15 @@ another extension according to your OS) and put it into the folder #+END_SRC After this, CLCXX needs to be initialized: + #+BEGIN_SRC lisp (cxx:init) #+END_SRC - Finally we add all the bindings from C​++ to Common Lisp in the - newly created package "CL-TEST". This step is comparable to the - loading of a file containing =(defcfun ...)= and such in CFFI: + Finally all the bindings from C​++ to Common Lisp are defined and + added to the newly created package "CL-TEST". This step is + comparable to the loading of a file containing =(defcfun ...)= and + such in CFFI: #+BEGIN_SRC lisp (cxx:add-package "CL-TEST" "TEST") @@ -316,24 +318,20 @@ another extension according to your OS) and put it into the folder In quicklisp this can be done like this: #+BEGIN_SRC bash - -$ ln -s /tmp/example ~/quicklisp/local-projects - + $ ln -s /tmp/example ~/quicklisp/local-projects #+END_SRC Then start up Common Lisp and load the project with: #+BEGIN_SRC lisp - -Cl-USER​> (ql:quickload "my-cl-lib") -To load "my-cl-lib": - Load 1 ASDF system: - my-cl-lib -; Loading "my-cl-lib" -............... -("my-cl-lib") -CL-USER​> - + Cl-USER​> (ql:quickload "my-cl-lib") + To load "my-cl-lib": + Load 1 ASDF system: + my-cl-lib + ; Loading "my-cl-lib" + ............... + ("my-cl-lib") + CL-USER​> #+END_SRC Open the file =/tmp/example/scratch.lisp= to see and explore From 84ccf6be4985ed8376b6eebc09c2782665208311 Mon Sep 17 00:00:00 2001 From: ormf Date: Sun, 12 Feb 2023 13:25:44 +0100 Subject: [PATCH 42/46] added links to CLCXX and quicklisp in README --- doc/README.org | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README.org b/doc/README.org index dbc7c1f..02d9300 100644 --- a/doc/README.org +++ b/doc/README.org @@ -15,11 +15,11 @@ * Quickstart - - Install CLCXX into /usr/local/lib + - Install [[https://github.com/Islam0mar/CLCXX][CLCXX]] into /usr/local/lib - copy the =example= directory to =/tmp/example= - - with quicklisp installed do + - with [[https://www.quicklisp.org/beta/][quicklisp]] installed do #+BEGIN_SRC bash $ ln -s /tmp/example ~/quicklisp/local-projects From 0c02b92d1e56ff174725d7f45fa06f63c6112c10 Mon Sep 17 00:00:00 2001 From: ormf Date: Mon, 13 Feb 2023 09:13:12 +0100 Subject: [PATCH 43/46] added lib building instructions to Quickstart --- doc/README.org | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/README.org b/doc/README.org index 02d9300..3bbfd02 100644 --- a/doc/README.org +++ b/doc/README.org @@ -19,6 +19,16 @@ - copy the =example= directory to =/tmp/example= + - Build the library with + +#+BEGIN_SRC bash +$ cd /tmp/examples/my-lib +$ mkdir build +$ cd build +$ cmake .. +$ make +#+END_SRC + - with [[https://www.quicklisp.org/beta/][quicklisp]] installed do #+BEGIN_SRC bash From 8b7a7fa7b82396d201087e39a447cfcf86bccd27 Mon Sep 17 00:00:00 2001 From: ormf Date: Mon, 13 Feb 2023 09:16:13 +0100 Subject: [PATCH 44/46] README edits --- doc/README.org | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/doc/README.org b/doc/README.org index 3bbfd02..ddda62b 100644 --- a/doc/README.org +++ b/doc/README.org @@ -22,11 +22,7 @@ - Build the library with #+BEGIN_SRC bash -$ cd /tmp/examples/my-lib -$ mkdir build -$ cd build -$ cmake .. -$ make +$ cd /tmp/examples/my-lib && mkdir build && cd build && cmake .. && make #+END_SRC - with [[https://www.quicklisp.org/beta/][quicklisp]] installed do From d82b4e90752df0933d1f9147a7bd99822f4e0059 Mon Sep 17 00:00:00 2001 From: ormf Date: Mon, 13 Feb 2023 09:18:33 +0100 Subject: [PATCH 45/46] fixed typos --- doc/README.org | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README.org b/doc/README.org index ddda62b..a352ff9 100644 --- a/doc/README.org +++ b/doc/README.org @@ -15,7 +15,7 @@ * Quickstart - - Install [[https://github.com/Islam0mar/CLCXX][CLCXX]] into /usr/local/lib + - Install [[https://github.com/Islam0mar/CLCXX][CLCXX]] into =/usr/local/lib= - copy the =example= directory to =/tmp/example= @@ -196,7 +196,7 @@ function =greet=. #+END_SRC Defines a class framework of the C​++ class =xx= for Common Lisp (named -="cl-xx"= in Common Lisp). This creates bindings for the Common Lisp +=cl-xx= in Common Lisp). This creates bindings for the Common Lisp destructor function =#'cl-test:destruct-cl-xx= #+BEGIN_SRC c From 02855f02cba50958232e0f5716f5a04b7f8bd1ed Mon Sep 17 00:00:00 2001 From: ormf Date: Mon, 13 Feb 2023 09:21:02 +0100 Subject: [PATCH 46/46] fixed typos in README --- doc/README.org | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/README.org b/doc/README.org index a352ff9..441e80f 100644 --- a/doc/README.org +++ b/doc/README.org @@ -267,9 +267,9 @@ another extension according to your OS) and put it into the folder =cl-cxx=. Before loading the custom library, the CLCXX library has to be loaded. The lisp code assumes the library is located in =/usr/local/lib=. Note that plain CLCXX from github will install it - in =~/.local/lib=. To install in /usr/local/lib instead, use =cmake - -DCMAKE_INSTALL_PREFIX=/usr/local ..= instead of =cmake ..= in the - build step of CLCXX. + in =~/.local/lib=. To install in =/usr/local/lib= instead, use + =cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..= instead of =cmake ..= + in the build step of CLCXX. First both libs are defined: