Here's something I'd like to submit to your analysis
before doing any changes. Compilation of cl-sdl
revealed the following bug in clisp:
src/foreign.d says abount equal_fvd():
"According to the ANSI C rules, two "c-struct"s are
only equivalent if they
come from the same declaration. Same for "c-union"s."
As a result, equal_fvd() is implemented in terms of EQ
rather than EQUALP when comparing the internal
representation of c-struct or c-union.
However, upon file-compilation of def-call-out, the
parse-c-function (i.e. the nested #(c-struct ...)
integrated into the .fas, which looses object identity.
With ffi forms across 2 compiled files, we obtain 2
different arrays even though the source code refers to
a single c-struct type definition (i.e. (def-c-type
event (c-union ...))).
As a result, completely valid code like
(def-c-type event (c-union ...))
(def-call-out poll-event (:arguments (c-pointer event)))
(with-foreign-object (e 'event (poll-event)))
fails with a conversion error raised by equal_fvd().
#<FOREIGN-VARIABLE "EXEC-ON-STACK" #xBFFF8CAC> kann
nicht in den Foreign-Typ #(C-STRUCT FOO
NIL #(I D) #<COMPILED-FUNCTION :LAMBDA> UINT8
DOUBLE-FLOAT) umgewandelt werden.
Work-around: do not load .fas file, use either .lisp or
(load :compiling t)
There are two solution paths I'd like to discuss:
A) Change def-call-out so as to not inline (expand
into) the result of parse-c-function and rather call
parse-c-type at load-time.
B) Implement equal_fvd() like EQUALP for union and structs.
C) Do both :-)
Note that A) may still lead to conversion errors when
redefining an already existing type while some other
code still refers to the original vector. Similar to
re-eval'ing defclass, although much harder to
understand from a user POV (s/he sees to
identical-looking type declarations as reported by
deparse-c-type and fails to understand why there's a
Note that B) causes additional run-time overhead for
each function call, which could be avoided if
EQ-identity were preserved (via A or C).
Whereas A) would just make loading slower -- presumably
invoking parse-c-function at load-time is slower than
reading a #() array, and needs some more thought upon
whether delegating that to load-time causes other
side-effects. BTW, note that the AFFI def-lib-call-out
macro expands to a literal parse-c-function instead of
its value -- it is not subject to this bug.
Actually, thinking again about the matter, there's not
even a need for 2 files.
(def-c-struct foo (i uint8) (d double-float))
(def-call-out fvd3 (:name "ffi_identity")
(:arguments (x (c-pointer foo)))
(:return-type (c-pointer foo)))
(defun fvd03 (n)
(with-foreign-object (x 'foo)
Loading this from a .fas is enough to trigger the bug.