lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


David Greenspan wrote:
> I'm trying to pass a struct by value to a varargs API call over the LuaJIT
> FFI, like so:
> 
> =====
> ffi.cdef [[
>     typedef void *id;
>     typedef void *SEL;
>     id objc_msgSend(id theReceiver, SEL theSelector, ...);
>     typedef struct _NSRect { double x, y, width, height; } NSRect;
> ]]
> local r, s, a, b, rect = ..., ffi.new("NSRect", {0,0,32,32}})
> ffi.C.objc_msgSend(r, s, rect, a, b)
> =====

Ouch. Passing structs by value is bad enough, but passing them to
varargs is really evil. Ok, so that's a side-effect of the central
dispatch model of ObjC.

BTW: ffi.new("NSRect", 0, 0, 32, 32) works as well.

> What happens is the arguments seem to get garbled, and as best I can tell,
> the reason is that the implementation of objc_msgSend is expecting to pull
> the NSRect struct off the stack, but LuaJIT is passing a pointer, because it
> "infers" that objc_msgSend is expecting a pointer based on the fact that I'm
> passing a struct.  In fact, LuaJIT seems to explicitly replace struct types
> with corresponding pointer types in ccall_ctid_vararg.

Yes, that's deliberate. And it's the only reasonable solution with
the current automatic conversion rules. Otherwise you'd have to
cast every struct to a pointer-to-struct before passing them to
varargs (most APIs only want to receive structs by reference).

> Is there some way to pass the struct by value?  Is there a good explanation
> for the current behavior?  I'm trying to avoid writing any C stubs or
> wrappers at all when calling C APIs. :)

Well, I don't know how to solve that right now. I'd need to add a
special pass-by-value cast or some such ... stumped ...

But there are two workarounds:

1. On x86, you can pass the components of the struct to simulate
pass-by-value, i.e.:

  ffi.C.objc_msgSend(r, s,   0, 0, 32, 32,   a, b)

This doesn't work on x64 because of the crazy rules for passing
structs by value in the x64 ABI.

2. Define a couple of function pointers with fixed args for those
methods that need struct-by-value args and assign objc_msgSend to
them. Then use the function pointers to call those methods.

[Technically using a fixarg prototype for a vararg function is
wrong on x64 (missing $al = nfpr). But ffi.C.objc_msgSend is only
a forwarder and the receiver is in fact a fixarg function.]

--Mike