Hello,
The SBCL/CMUCL ffi has a useful form of pointer handling, shown in the following example that defines the lisp function fits-open-file from the C function ffopen:
;; int ffopen(int *fptr, char *filename, int mode, int *status) (define-alien-routine ("ffopen" fits-open-file) int ;; ffopen returns INT (fptr unsigned-int :out) ;; a pointer to an unsigned int, that is set by function (filename c-string) (mode int) (status int :in-out)) ;; a pointer to an int, that is passed to function, and set by function
fptr and status are actually pointers, and the values that they point to are returned along with the INT that the function itself returns. But on the
lisp side, you never see their pointer nature - you just pass and accept normal integers, and the FFI boxes them up an creates pointers to them, passes the pointers to the C function, and unboxes them on return.
A call to this function including the collection of return values, takes the form
(multiple-value-bind (return-value-of-function fptr new-status) (fits-open-file filename mode status) .... )
fptr, as an :OUT pointer, is not passed to the function call, but status, as an :IN-OUT pointer, is passed to ffopen
The return values are the value of the function, and the values pointed to by the pointers fptr and status.
My question is: Is there any way to make CFFI handle pointers in a similar manner? How can I do the above in CFFI?
Many thanks, Jan
On Sat, 2007-01-13 at 22:36 -1000, J. T.K. wrote:
fptr, as an :OUT pointer, is not passed to the function call, but status, as an :IN-OUT pointer, is passed to ffopen
The return values are the value of the function, and the values pointed to by the pointers fptr and status.
My question is: Is there any way to make CFFI handle pointers in a similar manner? How can I do the above in CFFI?
The attached should give you a good general idea of how to do it. It is untested.
You can similarly handle out-arguments, but I don't think you can get away with dropping arguments to the lisp-function without wrapping defcfun in your package.
You just have to use (in-out :int) etc. as the argtype, and call the function like:
(call-with-in-out-arguments (lambda () (fits-open-file ...))
To do otherwise would change the argument translation semantics, which currently don't permit argument translators to change the result of calling a defcfun'd function. You could add a with-in-outs parametrized type to use as the rettype in defcfun to get around this, but only as long as defcfun and foreign-funcall continue to pass the real, complete call form to expand-type-from-foreign, as is done now.
I used private symbols. You could do it without private symbols, probably, but the current exported type translation interface breaks down when you want to parametrize your own types and have parameters affect translation.
I'll say here that I don't like the semantics of out and in-out foreign arguments, and I recommend using a wrapper macro that will more explicitly handle these results for you.
Stephen Compall s11@member.fsf.org wrote: ... The attached should give you a good general idea of how to do it. It is untested. ....
Thank you for the pointers.
I've managed to write a macro that does the wrapping that is necessary to implement sbcl-style :out and :in-out pointers using the tools in cffi. This is useful for porting sbcl/cmucl FFIs over to CFFI. Having always used SBCL in the past, I've always found the :OUT and :IN-OUT syntax of their FFI to be very nice because it allows one to hide a lot of pointer ugliness with just a single keyword. I wonder it something like this would be useful for CFFI defcfun? But as the attached macro code shows, it seems to be easy to implement atop CFFI.
Thanks again for the tips. Jan
;; example use of %define-alien-routine macro to implement :OUT and :IN-OUT args like SBCL
(defctype int :int) ;; define a new type to be more like SBCL
(macroexpand '(%define-alien-routine ("ffopen" fits-file-open) int (fptr int :out) (filename :string) (mode int) (status int :in-out)))
==> ;; expands to
(progn (declaim (inline %defcfun-ffopen)) (defcfun ("ffopen" %defcfun-ffopen) int (fptr :pointer) (filename :string) (mode int) (status :pointer)) (defun fits-file-open (filename mode status) (with-foreign-object (#:status2488 'int) (setf (mem-ref #:status2488 'int 0) status) (let ((#:mode2487 mode)) (let ((#:filename2486 filename)) (with-foreign-object (#:fptr2485 'int) nil (values (%defcfun-ffopen #:fptr2485
#:filename2486
#:mode2487
#:status2488) (mem-ref #:fptr2485 'int 0) (mem-ref #:status2488 'int 0))))))))