Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/bohonghuang/cffi-ops
A Common Lisp library that helps write concise CFFI-related code.
https://github.com/bohonghuang/cffi-ops
Last synced: 25 days ago
JSON representation
A Common Lisp library that helps write concise CFFI-related code.
- Host: GitHub
- URL: https://github.com/bohonghuang/cffi-ops
- Owner: bohonghuang
- License: apache-2.0
- Created: 2023-06-07T16:31:00.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2024-08-10T17:43:00.000Z (4 months ago)
- Last Synced: 2024-08-10T18:49:37.848Z (4 months ago)
- Language: Common Lisp
- Homepage:
- Size: 44.9 KB
- Stars: 31
- Watchers: 9
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.org
- License: LICENSE
Awesome Lists containing this project
- awesome-cl - cffi-ops - helps write concise CFFI-related code. (C ##)
README
#+TITLE: cffi-ops
Write CFFI stuff quickly without runtime overhead.
* Introduction
CFFI is powerful, but using its API to write C-style code can sometimes be cumbersome because it requires you to repeatedly pass in types,
unlike the dot operator in C that has some type inference capabilities.This library provides CFFI with dot operator-like functionality at compile time,
allowing you to write CFFI-related code as simple as C with just a small amount of FFI type declarations.This library has been tested to work on SBCL, CCL, ECL, ABCL, and CLISP,
and theoretically is portable across implementations that provide ~macroexpand-all~.
* Rules
Here is a comparison table between C syntax:| C | ~cffi-ops~ |
|--------------------------+--------------------------------------------------------------------------------|
| ~x->y.z~ or ~x->y->z~ | ~(-> x y z)~ (Note that ~x~, ~y~, and ~z~ must be the same symbols used in ~defcstruct~) |
| ~&x->y~ | ~(& (-> x y))~ |
| ~*x~ | ~([] x)~ |
| ~x[n]~ | ~([] x n)~ |
| ~&x[n]~ or ~x + n~ | ~(& ([] x n))~ |
| ~x.y = z~ | ~(setf (-> x y) z)~ if ~z~ is a variable |
| | ~(csetf (-> x y) z)~ if ~z~ is a CFFI pointer |
| ~A _a, *a = &_a~ | ~(clet ((a (foreign-alloca '(:struct A)))) ...)~ |
| ~A *a = malloc(sizeof(A))~ | ~(clet ((a (cffi:foreign-alloc '(:struct A)))) ...)~ |
| ~A _a = *b, *a = &_a~ | ~(clet ((a ([] b))) ...)~ |
| ~A *a = b~ | ~(clet ((a b)) ...)~ |Please note that since it is not possible to directly manipulate C compound types in Lisp,
binding and assignment of compound types require the use of ~clet~ (or ~clet*~) and ~csetf~,
which bind and operate on variables that are CFFI pointers.And the symbol ~->~ is directly exported from the [[https://github.com/hipeta/arrow-macros][arrow-macros]] package,
so this library is fully compatible with ~arrow-macros~,
which means you can freely use all the macros (including ~->~) provided by ~arrow-macros~ inside or outside of ~clocally~, ~clet~, ~clet*~, or ~csetf~.
* Example
For the following C code:#+BEGIN_SRC c
#include
#includetypedef struct {
float x;
float y;
float z;
} Vector3;typedef struct {
Vector3 v1;
Vector3 v2;
Vector3 v3;
} Matrix3;void Vector3Add(Vector3 *output, const Vector3 *v1, const Vector3 *v2) {
output->x = v1->x + v2->x;
output->y = v1->y + v2->y;
output->z = v1->z + v2->z;
}int main(int argc, char *argv[]) {
Matrix3 m1[3];
m1[0].v1.x = 1.0;
m1[0].v1.y = 2.0;
m1[0].v1.z = 3.0;
Matrix3 m2 = *m1;
Vector3 *v1 = &m2.v1;
Vector3 *v2 = malloc(sizeof(Vector3));
,*v2 = *v1;
v2->x = 3.0;
v2->z = 1.0;
Vector3Add(v1, v1, v2);
assert(v1->x == 4.0);
assert(v1->y == 4.0);
assert(v1->z == 4.0);
free(v2);
return 0;
}
#+END_SRCThe equivalent Lisp code (written using ~cffi-ops~) is:
#+BEGIN_SRC lisp
(defpackage cffi-ops-example
(:use #:cl #:cffi #:cffi-ops))(in-package #:cffi-ops-example)
(defcstruct vector3
(x :float)
(y :float)
(z :float))(defcstruct matrix3
(v1 (:struct vector3))
(v2 (:struct vector3))
(v3 (:struct vector3)))(defun vector3-add (output v1 v2)
(clocally
(declare (ctype (:pointer (:struct vector3)) output v1 v2))
(setf (-> output x) (+ (-> v1 x) (-> v2 x))
(-> output y) (+ (-> v1 y) (-> v2 y))
(-> output z) (+ (-> v1 z) (-> v2 z)))))(defun main ()
(clet ((m1 (foreign-alloca '(:array (:struct matrix3) 3))))
(setf (-> ([] m1 0) v1 x) 1.0
(-> ([] m1 0) v1 y) 2.0
(-> ([] m1 0) v1 z) 3.0)
(clet* ((m2 ([] m1))
(v1 (& (-> m2 v1)))
(v2 (foreign-alloc '(:struct vector3))))
(csetf ([] v2) ([] v1))
(setf (-> v2 x) 3.0
(-> v2 z) 1.0)
(vector3-add v1 v1 v2)
(assert (= (-> v1 x) 4.0))
(assert (= (-> v1 y) 4.0))
(assert (= (-> v1 z) 4.0))
(foreign-free v2))))
#+END_SRCAnd the equivalent Lisp code (written without using ~cffi-ops~) is:
#+BEGIN_SRC lisp
(defpackage cffi-example
(:use #:cl #:cffi))(in-package #:cffi-example)
(defcstruct vector3
(x :float)
(y :float)
(z :float))(defcstruct matrix3
(v1 (:struct vector3))
(v2 (:struct vector3))
(v3 (:struct vector3)))(declaim (inline memcpy))
(defcfun "memcpy" :void
(dest :pointer)
(src :pointer)
(n :size))(defun vector3-add (output v1 v2)
(with-foreign-slots (((xout x) (yout y) (zout z)) output (:struct vector3))
(with-foreign-slots (((x1 x) (y1 y) (z1 z)) v1 (:struct vector3))
(with-foreign-slots (((x2 x) (y2 y) (z2 z)) v2 (:struct vector3))
(setf xout (+ x1 x2) yout (+ y1 y2) zout (+ z1 z2))))))(defun main ()
(with-foreign-object (m1 '(:struct matrix3) 3)
(with-foreign-slots ((x y z)
(foreign-slot-pointer
(mem-aptr m1 '(:struct matrix3) 0)
'(:struct matrix3) 'v1)
(:struct vector3))
(setf x 1.0 y 2.0 z 3.0))
(with-foreign-object (m2 '(:struct matrix3))
(memcpy m2 m1 (foreign-type-size '(:struct matrix3)))
(let ((v1 (foreign-slot-pointer m2 '(:struct matrix3) 'v1))
(v2 (foreign-alloc '(:struct vector3))))
(memcpy v2 v1 (foreign-type-size '(:struct vector3)))
(with-foreign-slots ((x z) v2 (:struct vector3))
(setf x 3.0 z 1.0))
(vector3-add v1 v1 v2)
(with-foreign-slots ((x y z) v1 (:struct vector3))
(assert (= x 4.0))
(assert (= y 4.0))
(assert (= z 4.0)))
(foreign-free v2)))))
#+END_SRCBoth of them should generate almost equivalent machine code in SBCL and have very similar performance.