[Openmcl-devel] Calling foreign function with float array as argument?

Gary Byers gb at clozure.com
Thu Nov 21 15:02:13 PST 2002

On Thu, 21 Nov 2002, Ronnie Hoogerwerf wrote:

> > There are a couple of approaches that would work in current and (near)
> > future versions of OpenMCL:
> >
> > a) Copy the contents of the lisp vector of single floats to a C vector
> > of single floats.  Pass the C vector to foreign code; copy the C vector's
> > contents back into the lisp vector after the foreign call returns.
> >
> > b) Allocate the lisp vector in the C heap; pass the (fixed) address of
> > its first element to foreign code (which can then side-effect it directly.)
> > "Manually" dispose of the lisp vector when you're through with it.
> >
> > If you're interested, I'll try to explain (a) and (b) above in greater
> > detail.
> Thanks for the reply, I think I have some new ideas to try out now. However,
> I would appreciate it if you could go into some more detail on the suggestion
> you described above (and maybe give rough example of how to implement this).
> Thanks again.

This is the idea behind (a): it stack-allocates a C vector of single
floats, copies the contents of a lisp vector to that C vector, executes
an arbitrary body of code (perhaps a foreign function call), then optionally
copies the (possibly modified) floating-point values from the C vector back
into the lisp vector.  (That is, it does all of this unless I misspelled
or misparenthesized something: I don't have an OpenMCL in front of me
at the moment.)

(defmacro with-c-single-float-vector ((c-vector lisp-vector &optional
					copy-back) &body body)
  (let* ((nbytes (gensym))
         (cv (gensym))
         (lv (gensym)))
    `(let* ((,lv ,lisp-vector)
            (,nbytes (* 4 (length ,lv))))
       (ccl::%stack-block ((,cv ,nbytes))
         ;; Copy contents of the lisp vector to the C vector:
         ;;  copy from byte offset 0 to byte offset 0.
         (ccl::%copy-ivector-to-ptr ,lv 0 ,cv 0 ,nbytes)
         (let* ((,c-vector ,cv))
           (multiple-value-prog1   ; overkill, probably
             (progn , at body)
             ,@(when copy-back
              `((ccl::%copy-ptr-to-ivector ,cv 0 ,lv 0 ,nbytes)))))))))

(b) is a lot simpler, at least syntactically:

;;; Allocate a lisp vector of N SINGLE-FLOATs in the foreign heap;
;;; return the array and a MACPTR to its 0th element.  The array will
;;; never move, but will never get GCed either.

(defun make-heap-single-float-vector (n)
  (ccl::%make-heap-ivector arch::subtag-single-float-vector (* 4 n) n))

When "done" with that vector (assuming that the concept applies),
dispose of it:

(ccl::%dispose-heap-ivector v)

The function CCL::%MAKE-HEAP-IVECTOR could stand to be dressed up some:
it takes as arguments an "array type code", the vector's size in (8-bit)
bytes, and the vector's size in elements.  (The size in bytes could obviously
be computed from the size in elements; the former's required, the latter's
optional but an error will be signalled if it's not provided.)  I would
say "hey, don't blame me, I didn't write this" but I have a hunch that
I may have ...

Anyhow, baroque as its calling conventions may be, it works; it's used
by the code that implements many types of streams (so the buffer is
"nailed down" and can be passed to the OS, but can also be accessed as
a lisp string or byte vector.)  The good news is that these things aren't
seen by the GC and so will never move; the bad news is that these things
aren't seen by the GC and therefore one has to manage their allocation/
deallocation "manually".

Openmcl-devel mailing list
Openmcl-devel at clozure.com

More information about the Openmcl-devel mailing list