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

Gary Byers gb at clozure.com
Thu Nov 21 17:08:38 UTC 2002

On Thu, 21 Nov 2002, Ronnie Hoogerwerf wrote:

> Hi,
> I relatively new to openmcl but have some experience using mcl and cmucl.
> I am trying to port a lisp interface to the PGPLOT graphics library to
> openmcl (the orignal was written for cmucl) and am running into some
> problems that I cannot seem to solve. I need to pass an array (of type
> single-float) to a foreign function. This function then changes the values
> of the array and I would like to retrieve those changed values.
> An example of the c-code looks something like:
> /****************************************/
> #include "cpgplot.h"
> #include <string.h>
> extern int pgcurs_();
> int cpgcurs(float *x, float *y, char *ch_scalar)
> {
>   int len_ch_scalar = 1;
>   int r_value;
>   r_value = pgcurs_(x, y, ch_scalar, len_ch_scalar);
>   return r_value;
> }
> /****************************************/
> The cmucl code solves the problem of passing pointers to float by
> obtaining the memory address of the lisp array (using
> kernel:get-lisp-obj-address) and then passes the address to the function.

That solution would (in general) fail if a GC happened during the execution
of the foreign function: the garbage collector could move the lisp vector,
and would have no way to tell C code where it was moved to.

You might be tempted to say that there's no way that a GC could happen
during the execution of your foreign function call.  That's probably
true currently, but as OpenMCL moves to using preemptively scheduled
native threads, it becomes impossible to make that assumption: a GC
could occur (because of activity of another thread) on essentially any
instruction boundary.  There's therefore intentionally no (supported)
way to pass the address of a lisp object to foreign code.

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.

Both of these appoaches are more awkward than simply passing the lisp
object's address to foreign code would be; both of these approaches
are GC-safe (and will continue to be), whereas pretending that the
addresses of lisp objects are fixed would only be safe if it were
true.  It isn't.  (Some months ago, I exchanged several email messages
with someone who was trying to use CCL::%ADDRESS-OF in the simple,
straightforward way in MCL, but found that it sometimes failed.  It
was several messages before we realized that the addresses in question
and some other arguments in the call were bignums: the addresses of
the lisp objects he was passing were changing before they even got
to the foreign code.)

> I am at a loss on how I do this in openmcl. So my questions are:
> 1) How do I call the foreign function?
>    a) (ccl::external-call "_cpgcurs"
> 		          (:* float) x
> 		          (:* float) y
> 	     	          (:* char) c
> 		          :int)
>    b) (ccl::external-call "_cpgcurs"
> 		          :address x
> 		          :address y
> 	     	          :address c
> 		          :int)
> 2) How do I obtain the memory address (or macptr) of a lisp object?

The lisp printer uses CCL::%ADDDRESS-OF.  It's best to think of the
32-bit address it returns as "a probably unique identifier which makes
it easier to distinguish otherwise anonymous objects" rather than as
something too closely related to the object's actual address.

>    Or is there some other way around this?

If you're interested, I'll try to explain (a) and (b) above in greater

> I am using openmcl 0.13 on Mac OS X 10.1.5.
> I hope I make myself clean and I would appreciate any help.
> Thanks,
> Ronnie
> P.S. I succeeded in calling a foreign function which had just a single
>      float as its argument (not an array) but was unable to modify this
>      to work with an array of floats:
> (defun func (x)
>   (rlet ((_x :float))
>     (setf (pref _x :float) x)
>     (ccl::external-call "_func"
>   			(:* float) _x)
>     (pref _x :float)))

The idea with (a) is sort of an extension of this.

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

More information about the Openmcl-devel mailing list