#256 ffi refuses c-struct conversion

lisp error
Jörg Höhle
ffi (23)
Jörg Höhle

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
value of
parse-c-function (i.e. the nested #(c-struct ...)
array) gets
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
File A:
(def-c-type event (c-union ...))
(def-call-out poll-event (:arguments (c-pointer event)))
File B:
(with-foreign-object (e 'event (poll-event)))
fails with a conversion error raised by equal_fvd().

nicht in den Foreign-Typ #(C-STRUCT FOO
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
conversion error).

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)
(fvd3 x)))
Loading this from a .fas is enough to trigger the bug.

Jörg Höhle.


  • Jörg Höhle
    Jörg Höhle

    Logged In: YES

    Now that same issue hit me trying to use cl-gd.
    I just realized that a recursive EQUALP cannot work, since
    it may lead to infinite recursion for some structures (those
    with :pointer-self, e.g. linked list style structure etc.).

  • Jörg Höhle
    Jörg Höhle

    Logged In: YES

    Sorry, my patch is not ready yet. I realized late that some
    PARSE-C-FUNCTION might be unavoidable at some early time, since the
    compiler wants to know the function's signature.
    Initially, I wanted to fix several problems at once: do not perform
    side-effects during macroexpansion (such as setting the default foreign
    language), reduce number of calls to parse-c-function etc.
    Now that'll have to wait until at east after my vacation.

  • Jörg Höhle
    Jörg Höhle

    Logged In: YES

    thank you for your bug report.
    the bug has been fixed in the CVS tree.
    you can either wait for the next release (recommended)
    or check out the current CVS tree (see http://clisp.cons.org\)
    and build CLISP from the sources (be advised that between
    releases the CVS tree is very unstable and may not even build
    on your platform).

  • Jörg Höhle
    Jörg Höhle

    • status: open --> closed