[Openmcl-devel] dfsl/foreign function oddities
Gary Byers
gb at clozure.com
Fri May 13 12:02:07 PDT 2005
One potential source of confusion that sometimes leads to symptoms
like those that you describe stems from the fact that #_ is a reader
macro (and therefore does most of its work at read-time.). The act of
reading #_foo has the (important!) side-effect of defining a global
macro function on the symbol OS::|foo| as well as the effect of
returning that symbol.
If you compile a source file that does something like:
(defun allocate-initialized-pointer (n ch)
"Allocate foreign memory, fill it with the specified character value"
(let* ((p (#_malloc n)))
(unless (%null-ptr-p p)
(#_memset p (char-code ch) n)
p)))
then the side-effects of defining the macros OS::|malloc| and OS::|memset|
take place just prior to the time that the definition is compiled. READ
will return something like:
(DEFUN ALLOCATE-INITIALIZED-POINTER (N CH)
"Allocate foreign memory, fill it with the specified character value"
(LET* ((P (OS::|malloc| N)))
(UNLESS (%NULL-PTR-P P)
(OS::|memset| P (CHAR-CODE CH) N)
P)))
(this is what the compiler sees), and the macros OS::|malloc| and
OS::|memset| will have just been defined and things should work as
expected.
It's sometimes tempting to define a little utility like the example
as a macro:
(defmacro allocate-initialized-pointer (n ch)
"Allocate foreign memory, fill it with the specified character value"
(let* ((p (gensym)))
`(let* ((,p (#_malloc ,n)))
(unless (%null-ptr-p ,p)
(#_memset ,p (char-code ,ch) ,n)
,p))))
If you store that macro definition if file A.lisp, then the read-time
side-effects of defining global macro functions on OS::|malloc| and
OS:|memset| will occur -when A.lisp is compiled- (or, slightly more
accurately, when A.lisp is read.) The FASL file will store the
compiled equivalent of:
(DEFMACRO ALLOCATE-INITIALIZED-POINTER (N CH)
"Allocate foreign memory, fill it with the specified character value"
(LET* ((P (GENSYM)))
`(LET* ((,P (OS::|malloc| ,N)))
(UNLESS (%NULL-PTR-P ,P)
(OS:|memset| ,P (CHAR-CODE ,CH) ,N)
,P))))
but nothing in the FASL file does anything to ensure that those
symbols (OS::|malloc|/OS::|memset|) have global macro definitions (that
step happens as a side-effect of the #_ reader macro.)
If B.lisp contains something like:
(let* ((block-of-x (allocate-initialized-pointer 256 #\x)))
...)
then whether or not that works depends on whether or not A.lisp and B.lisp
were compiled in the same session; if not, then it's likely that the OS
package symbols won't have any kind of global definitions and the compiler
will compile calls to unknown (and nonexistent) functions.
Like I said, that scenario would explain the symptoms that you describe;
I think that the manual actually tries to explain the issue, but the prose
may be a little terse and cryptic.
An example of a workaround is the macro MAKE-RECORD (defined in
"ccl:lib;macros.lisp"); it goes to great and somewhat ugly lengths to
ensure that read-time side-effects take place:
(defmacro make-record (record-name &rest initforms)
(let* ((ftype (%foreign-type-or-record record-name))
(bits (ensure-foreign-type-bits ftype))
(bytes (if bits
(ceiling bits 8)
(error "Unknown size for foreign type ~S."
(unparse-foreign-type ftype))))
(p (gensym))
(bzero (read-from-string "#_bzero")))
`(let* ((,p (malloc ,bytes)))
(,bzero ,p ,bytes)
,@(%foreign-record-field-forms p ftype record-name initforms)
,p)))
An alternative to that sort of READ-FROM-STRING nonsense is to avoid
the use of #_ in macro expansions, e.g.:
(defmacro allocate-initialized-pointer (n ch)
"Allocate foreign memory, fill it with the specified character value"
(let* ((p (gensym)))
`(let* ((,p (external-call "_malloc" :unsigned-fullword ,n :address)))
(unless (%null-ptr-p ,p)
(external-call "_memset" :address ,p
:unsigned-byte (char-code ,ch)
:unsigned-fullword ,n
:void)
,p))))
On Fri, 13 May 2005, Chris Curtis wrote:
> I'm having some dfsl weirdness with foreign functions.
>
> I have one asdf system with code that calls #___CFStringMakeConstantString.
> That system is then loaded by another asdf system (via :depends-on). If I
> clean out all the dfsls in both systems and build, everything works great. If
> I build again, I get compiler warnings:
>
> ; Undefined function OS::|__CFStringMakeConstantString| (2 references)
>
> and, unsurprisingly, at runtime I get:
>
> Error: Undefined function OS::|__CFStringMakeConstantString| called with
> arguments (#<A Mac Pointer #xCAF40>) .
>
> This can be easily corrected by deleting *just* the dfsl that references
> #___CFStringMakeConstantString (or any dfsl that it depends on and thus
> triggers a recompile) -- voilà, it works again.
>
> I suspect that this might have something to do with the fact that
> __CFStringMakeConstantString is not officially a published API, since it's
> the only function that gets this error. I'm just not knowledgeable enough
> about the guts of the #_ interface to even know where to start.
>
> Any ideas?
>
> --chris
>
>
>
>
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel
>
>
More information about the Openmcl-devel
mailing list