[Openmcl-devel] Question about foreign-functions
gb at clozure.com
Fri Nov 21 19:43:16 UTC 2003
On Fri, 21 Nov 2003, Camille Troillard wrote:
> Hello OpenMCL,
> I am Camille, and I am new to this list.
> I am building a web server application with PortableAllegroServe with
> webaction and CL-SQL with OpenMCL on Mac OS X 10.3.
> I am getting some problems with FFI : Here is a code sample that
> doesn't work. It is very likely that I am wrong, because I just start
> writing Lisp programs.
> This function is supposed to call the function "stat", access the
> st_ino field of the structure and return the result which is
> (supposedly) an int.
> (defun filesys-inode (path)
> (let ((checked-path (probe-file path)))
> (cond (checked-path
> (rlet ((pstat (:* (:struct stat))))
> (with-cstrs ((str (namestring checked-path)))
> (#_stat str pstat))
> (pref pstat :stat.st_ino)))
> (t (error "path ~s does not exist" path)))))
> If I call this function I get this error :
> > Error in process listener(1): value 0 is not of the expected type
> > While executing: "Unknown"
RLET takes a sequence of variable name/foreign type pairs and executes
its body with each variable bound to a temporary (stack-allocated) pointer
to an instance of the corresponding foreign type.
When you say "(rlet ((pstat (:* (struct stat)))) ...)", you're basically
saying "let PSTAT be a pointer to a pointer to a STAT structure." That's
a perfectly legal and meaningful thing to say, but it's not what you
mean here; all that's being allocated is an uninitialized "pointer to
a STAT structure", and PSTAT is then made to point at that uninitialized
To look at this another way:
(rlet ((pstat :stat)) ...)
correctly allocates a :stat structure, which is 96 bytes wide on OSX,
and makes PSTAT point at that chunk of stack-allocated memory.
(rlet ((pstat (:* (:struct :stat)))) ...)
incorrectly allocates a 4-byte pointer, and makes PSTAT point at the
4-byte chunk that was allocated to hold that pointer.
Passing a pointer to a 4-byte chunk of memory to a function (#_stat)
that expects to be able to write into a 96-byte chunk of memory overwrites
that 4-byte chunk and the next 92 bytes that happen to be nearby it on
the stack. When your code returns to the read-eval-print loop, it
says something nonsensical; it's kind of amazing that it can say
anything at all, since there's probably about half of a :STAT structure
lodged in its stack frame ...
> If I put a "print" form around the pref call, the result I correctly
> printed on the standard output and then the error arises. A look to
> the backtrace confirms that the error occurs outside of the
> "filesys-inode" call.
The values that #_stat wrote into what it thought was a pointer to
a :stat structure are certainly there. (Some local variable values
in the next few stack frames just got overwritten.)
> If I replace rlet with rletz I get the following compilation error
> which I don't understand:
> > Error in process listener(1): While compiling FILESYS-INODE :
> > #1=(PSTAT (:* (:STRUCT STAT))) is not a
> symbol or lambda expression in the form (#1#) .
> I think that I must pass #_stat a reference to a stack allocated
> The problem is that:
> 1. I don't know how to pass a reference to a stack allocated object to
> 2. I don't know how to declare a stat structure object because if I
> try :
> (rlet ((pstat (:struct stat)))
It should probably work to say that; RLET's not too consistent about
how it parses the foreign type specifier: it wants to give preference
to "record" (struct/union) names over type names (which aren't necessarily
equivalent), but doesn't handle some compound foreign type specs (like
(:struct ...)) correctly. It'd be more consistent if it did, but you
can simply use the record name:
(rlet ((pstat :stat)) ...)
and that's exactly what you want to do here.
> I get a compilation error.
> Thanks in advance for any help!
> Kind Regards,
> Camille Troillard
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
More information about the Openmcl-devel