[Openmcl-devel] Foreign Function Questions

Hamilton Link hamlink at comcast.net
Sat Nov 25 11:03:16 PST 2006


I should take this opportunity to point to the opengl-ffi.lisp and 
rubix/*.lisp files in the example directory that comes with openmcl.  
While not much more than a handful of convenience macros and classes, 
they are still reasonably useful.  For example in rubix/blocks.lisp and 
rubix/lights.lisp are lisp class abstractions such as a light class, an 
class of object that can be translated and rotated, etc.

I don't know if these abstractions are useful to anyone other than me, 
but they're there.

With regards to objective C, the cocoa way of doing OpenGL is to set up 
an NSOpenGLView (or several) and set one or another of these views to 
be the opengl drawing context.  Then the following sequence of opengl 
commands will be rendered to that context.  I found the easiest thing 
to do was to mostly write straight opengl calls (particularly since the 
rubix demo was originally entirely in C++) and extract macros and 
classes when I saw idiomatic things conducive to reuse.

There are more substantial non-lisp abstraction libraries written on 
top of OpenGL.  A lisp port of those might be extremely interesting, 
although I don't know if it would have a large audience.

h

On Nov 25, 2006, at 11:29 AM, Brent Fulgham wrote:

> Gary,
>
> Thanks for that (as always) very detailed explanation.  In looking
> over the various incomplete OpenGL offerings, most of them make use
> of the UFFI to bind to either SDL or OpenGL.  I'm not sure if it
> would be better to attempt to port one of these libraries over to
> OpenMCL, or to make use of the native bindings.  Since I prefer
> OpenMCL's close integration with the Aqua and Objective C bindings, I
> would prefer a similar binding for OpenGL.  So perhaps the effort is
> worth it.
>
> The current binding is a thin interface to the underlying C api.  It
> might make sense to provide a Lisp-level library with some
> convenience functions to make interacting with the C core a bit
> easier.  Your function to produce the stack-based raw data for the C
> call would be a good candidate for this, as would the "opengl:with-
> matrix-mode" macro in the example OpenGL that comes with OpenMCL.
>
> I'll see if I can write a few simple helper functions/macros to make
> these steps easier.
>
> Thanks,
>
> -Brent
>
>
> On Nov 24, 2006, at 11:15 PM, Gary Byers wrote:
>
>>
>>
>> On Fri, 24 Nov 2006, Brent Fulgham wrote:
>>
>>> I have a couple of questions regarding the FFI and interface
>>> databases.  Perhaps this is covered in the documentation, but an
>>> initial review did not turn up what I was looking for.
>>>
>>> 1.  Is there a way to get the correct "decorated" names of the FFI
>>> functions from the database?  While it's pretty easy (in many cases)
>>> to guess from the original C header, I can't always figure out how
>>> things like arrays and other pointer types are expected to be passed.
>>
>> When dealing with foreign function call arguments and results,
>> OpenMCL's FFI basically deals with:
>>  - signed and unsigned integers of width 8/16/32/64
>>  - single and double floats
>>  - aggregate objects (structures/unions) that're passed by value
>>    on some platforms
>>  - pointers.
>>
>> If you ignore the issue of vector (AltiVec/XMM) types, that's
>> pretty much what C deals with.  Note that the difference between
>> an array type and a pointer type in C is often pretty subtle,
>> and it's often possible to use arrays an pointers interchangably.
>>
>> I don't remember whether the interface database stores syntactically
>> richer type information; by the time #_ has looked up a foreign
>> function definition, everything's boiled down to one of those
>> basic types (:signed-halfword, :address, etc.)
>>
>>>
>>> 2.  I would like to define some materials for the OpenGL FFI
>>> interface.  In C, this might look something like:
>>>
>>>      glMaterialfv(GL_BACK, GL_DIFFUSE, {0.396, 0.74151, 0.69102,
>>> 1.0});
>>>
>>>      But if I attempt to call it using something like:
>>>
>>>       (#_glMaterialfv #$GL_FRONT #$_GL_AMBIENT #(0.396D0 0.74151D0
>>> 0.69102D0 1.0D0))
>>>
>>> I get an error:  "> Error: value #<SIMPLE-VECTOR 4> is not of the
>>> expected type MACPTR."
>>>
>>> What's the proper way to declare this in the FFI description?
>>
>> Suppose that we wanted to call the C library function #_open, which
>> will return a file descriptor (small non-negative integer) or -1
>> (error
>> indicator) given a file namestring and an integer which specifies some
>> mode flags.  If we did:
>>
>> ? (#_open "/etc/passwd" #$O_RDONLY) ; open an existing file
>>                                                     ; for reading
>> we'd get a similar error (a complaint that the string "/etc/passwd"
>> isn't a MACPTR).
>>
>> In C, a string (for all intents and purposes; it could be argued that
>> C doesn't have a "string" data type, just a few sets of conventions)
>> is just the address of a sequence of 8-bit bytes with a #\nul byte
>> at the end.
>>
>> In Lisp in general (and in OpenMCL in particular), a string is ...
>> something else entirely.  In particular, it has some bits (somewhere)
>> that say that it's a string, and it probably has some bits (somewhere)
>> that say how many elements it contains, as well as some bits (or the
>> absence of some bits) that say whether or not it's a SIMPLE-STRING,
>> and whether or not it has a fill pointer and other things ... To
>> the extent that it has an "address", that address is generally
>> determined by the GC and can change at any instruction boundary.
>> (There are exceptions to this general rule.)
>>
>> What we sort of want to do in order to be able to call #_open is
>> to:
>>
>> a) allocate a block of non-relocatable memory somewhere.  Since
>>    we'll only need that block of memory until we're done calling
>>    #_open, a stack would be a good place to allocate it.  The
>>    block of memory needs to be of length N+1, where N is the
>>    length of the lisp string.
>>
>> b) copy the character codes of the characters in the lisp string
>>    to successive 8-bit bytes in the memory block.
>>
>> c) slap a trailing #\Nul byte on the end of the memory block
>>
>> d) pass the address of that memory block as the first argument to
>>    #_open.
>>
>> This is a common enough idiom that there's a macro that does most
>> of it for is (WITH-CSTRS).  If we used WITH-CSTRS when attempting
>> to call #_open, as in:
>>
>> (with-cstrs ((c-name "/etc/passwd"))
>>   (#_open c-name #$O_RDONLY))
>>
>> the FFI shouldn't complain and we should get a non-negative
>> file descriptor back from the call to #_open.
>>
>> Back to your example: I'm not entirely sure that the best lisp
>> interface to #_glMaterialfv would involve passing a lisp array
>> around, and if it did there might be some question as to whether
>> an array of element-type SINGLE-FLOAT should be used or whether
>> the interface we'd want should be more general.  Assuming that
>> we decided to lisp SINGLE-FLOAT vector around, we'd wind up
>> with a similar set of issues as existed in the string case:
>>
>> In C, an array of SINGLE-FLOATs is just the address of a block
>> of memory that contains some bits that can be interpreted as
>> SINGLE-FLOATs (or what C would probably just call a "float").
>>
>> In Lisp (especially in OpenMCL), a (VECTOR SINGLE-FLOAT) is something
>> else entirely; as in the case of a Lisp STRING, it has a lot of
>> auxiliary information about it encoded in bits that are part of
>> the object, and (as in the string case) we can't meaningfully talk
>> about the "address" of a (VECTOR SINGLE-FLOAT) in general without
>> running into GC issues.
>>
>> We'd have to go through essentially the same steps (stack-allocate
>> some foreign memory, copy the values in the lisp SINGLE-FLOAT array
>> into the foreign memory block, then pass the address of that block
>> to #_glMaterialfv).  Doing that would be simpler and less error-prone
>> if there was a WITH-FOREIGN-SINGLE-FLOAT-VECTOR (analogous to WITH-
>> CSTRs), but ... there isn't.  If we were going to do a lot of
>> this and had thought about if or how a lisp vector was the best
>> way to represent the material matrix and had thought about whether
>> a specialize vector type should be used or whether we wanted to
>> be more general about it, we might find it useful to write
>> WITH-FOREIGN-SINGLE-FLOAT-VECTOR.  (We'd also find it easier to
>> write if OpenMCL offered some syntactic sugar for dealing with
>> foreign arrays; without that, the code looks uglier and is probably
>> more error-prone as well.)
>>
>> A less-than-general approach that'd do the right thing in this
>> particular case would be something like:
>>
>> (rlet ((c-float-array (:array :float 4)))
>>   ;; We happen to know that a single-float value is 32-bits/4 bytes
>>   ;; wide, so we can multiply each array index by 4 to get a byte
>>   ;; index.  The FFI could do this for us.
>>  (dotimes (i 4)
>>    (setf (%get-single-float c-float-array (* i 4))
>>          (aref lisp-float-array i)))
>>  (#_glMaterialfv #$GL_FRONT #$_GL_AMBIENT c-float-array))
>>
>>>
>>> Thanks,
>>>
>>> -Brent
>>> _______________________________________________
>>> Openmcl-devel mailing list
>>> Openmcl-devel at clozure.com
>>> http://clozure.com/mailman/listinfo/openmcl-devel
>>>
>>>
>
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel
>




More information about the Openmcl-devel mailing list