Hello,
I've been working on an interface for the graphics package plplot (http://plplot.sourceforge.net/). Its API has lots of functions that expect arrays, matrixes and associated variables that specify the size of the array/matrix. To facilitate wrapping these functions I wrote a macro that I thought might (after some modification/cleaning) be useful for others. The macro can be found at: http:// paste.lisp.org/display/18274.
It was written to expand a function declaration like:
(pl-defcfun ("c_plmesh" plmesh) :void (x *plflt nx) (y *plflt ny) (z **plflt (nx ny)) (nx plint) (ny plint) (opt plint))
into: (progn statements removed)
(DEFCFUN ("c_plmesh" C-PLMESH) :VOID (X *PLFLT) (Y *PLFLT) (Z **PLFLT) (NX PLINT) (NY PLINT) (OPT PLINT))
(EXPORT 'C-PLMESH (PACKAGE-NAME *PACKAGE*)))
(DEFUN PLMESH (X Y Z OPT) (LET ((C-X (MAKE-PTR X :DOUBLE #'(LAMBDA (X) (COERCE X 'DOUBLE- FLOAT)))) (C-Y (MAKE-PTR Y :DOUBLE #'(LAMBDA (X) (COERCE X 'DOUBLE- FLOAT)))) (NX (ARRAY-DIMENSION Z 0)) (NY (ARRAY-DIMENSION Z 1)) (C-Z (MAKE-MATRIX Z))) (UNWIND-PROTECT (C-PLMESH C-X C-Y C-Z (FUNCALL #'(LAMBDA (X) (ROUND X)) NX) (FUNCALL #'(LAMBDA (X) (ROUND X)) NY) (FUNCALL #'(LAMBDA (X) (ROUND X)) OPT)) (PROGN (FOREIGN-FREE C-X) (FOREIGN-FREE C-Y) (FREE-MATRIX C-Z (LIST NX NY))))))
(EXPORT 'PLMESH (PACKAGE-NAME *PACKAGE*))))
Where make-ptr, make-matrix and free-matrix are functions that make 1D arrays, make 2D arrays & free 2D arrays respectively.
best, -Hazen
hbabcockos1@mac.com writes:
(pl-defcfun ("c_plmesh" plmesh) :void (x *plflt nx) (y *plflt ny) (z **plflt (nx ny)) (nx plint) (ny plint) (opt plint))
This is interesting and I think this is doable using plain defcfun and CFFI's type system. If it's not, it should.
(in-package #:cffi)
(defclass my-array (foreign-type-alias) ((dimensions :initarg :dimensions :reader dimensions)))
(define-type-spec-parser my-array (type &rest dimensions) (make-instance 'my-array :actual-type (parse-type type) :dimensions dimensions))
(defmethod expand-type-to-foreign-dyn (value var body (self my-array)) `(let ((,var (generate-array (list ,@(dimensions self)) ,value))) (unwind-protect (progn ,@body) (free-array ,var))))
(defcfun "plmesh" :void (x (my-array :float nx)) ; tk me a whl to fgr that plflt (y (my-array :float ny)) ; probably means float, heh (z (my-array :float nx ny)) (nx :int) (ny :int) (opt :int))
The macro expansion of the DEFCFUN above looks something like this:
(DEFUN PLMESH (X Y Z NX NY OPT) (LET ((#:G1046 (GENERATE-ARRAY (LIST NX) X))) (UNWIND-PROTECT (PROGN (LET ((#:G1047 (GENERATE-ARRAY (LIST NY) Y))) (UNWIND-PROTECT (PROGN (LET ((#:G1048 (GENERATE-ARRAY (LIST NX NY) Z))) (UNWIND-PROTECT (PROGN (LET ((#:G1049 NX)) (LET ((#:G1050 NY)) (LET ((#:G1051 OPT)) (VALUES (%FOREIGN-FUNCALL "plmesh" :POINTER #:G1046 :POINTER #:G1047 :POINTER #:G1048 :INT #:G1049 :INT #:G1050 :INT #:G1051 :VOID)))))) (FREE-ARRAY #:G1048)))) (FREE-ARRAY #:G1047)))) (FREE-ARRAY #:G1046))))
So, again, your example is quite interesting because it reminds me that we should export all of the functionality of the type system. In particular, the ability to get at type arguments in the type translators seems quite useful.
(Also, Greg Pfeil has been running into the "limitation" that typedefs don't behave like other user-defined types, such as structs and enums, with regard to type translators.)
I've been thinking about how to nicely export this kind of functionality to the user and this is what I came up with:
;;; thin wrapper around defclass with cffi::foreign-type as a super (define-foreign-type foo () (slot1 slot2) (:documentation "foo"))
(defmethod parse-type ((name (eql 'foo)) args) (destructuring-bind (bar &key baz) args (make-instance 'foo ...)))
Possibly with some syntactic sugar over that defmethod:
(define-parse-method foo (bar &key baz) (make-instance 'foo ...))
And the default method could be something like this:
(defmethod parse-type (name args) (apply #'make-instance name args))
Then we need to think about how typedefs would fit into this more CLOS-based type interface. Hopefully, we'd unify the translate/expand-[type-|]to/from-foreign{-dyn} pairs (ie. those with "type" in the name and those without).
Last but not the least, your example reminds me that we need to add an array type and to finish the shareable vectors interface, etc...
So, thanks! :-)
hbabcockos1@mac.com writes:
(pl-defcfun ("c_plmesh" plmesh) :void (x *plflt nx) (y *plflt ny) (z **plflt (nx ny)) (nx plint) (ny plint) (opt plint))
[...]
(DEFUN PLMESH (X Y Z OPT)
Ah, I missed this detail. To do this you need a wrapper function indeed. I suppose that when someone adds :out types we might come up with a generalized interface to omit arguments, maybe.