Calling a struct-by-value by reference

Attila Lendvai attila at lendvai.name
Wed Dec 23 22:18:45 UTC 2015


hi Liam,


> Re: https://bugs.launchpad.net/cffi/+bug/1528719
>
> (There is an error in the sample code, the struct is called
> 'struct-pair, not 'pair.)


thanks, fixed.


> Here is a simpler exhibition of the problem
>
> (sumpair (convert-to-foreign '(40 . 2) '(:struct struct-pair)))
> The value #.(SB-SYS:INT-SAP #X7FFFEC003560) is not of type LIST.
>    [Condition of type TYPE-ERROR]
>
> So, you've said the function is call-by-value, and then you want to
> pass a pointer to the structure, and it makes sense there's an error.


well, that depends on the perspective you take. as a user i certainly
didn't use a '(:pointer (:struct struct-pair)) in my original
WITH-FOREIGN-OBJECTS example, so i'm telling CFFI to deal with
structs, not pointers.

but from the perspective of the implementation it will of course
always be a pointer, because that's how the lisp side can deal with
foreign objects.

your example above is somewhat trickier, but there's nothing that
stops CFFI from passing around foreign structs as pointers internally
(which is an implementation detail), and deal with them either as
pointers or as values based on the ffi type info.


> You have to assume a translator, because that's the only way to have a
> structure by value.


i don't see why you say that (or maybe what you mean by
'translator').

we can expand into code that dispatches on the value type at runtime
(minus the , at body duplication and the wrong dispatch type):

(defmethod expand-to-foreign-dyn-indirect :around
    (value var body (type translatable-foreign-type))
  (let ((*runtime-translator-form*
          `(if (typep ,value 'foreign-pointer)
               (let ((,var ,value))
                 , at body)
               (with-foreign-object (,var ',(unparse-type type))
                 (translate-into-foreign-memory ,value ,type ,var)
                 , at body))))
    (call-next-method)))

i don't see how to add this cleanly to the current codebase, but with
this every relevant test succeeds.


> (defcfun "sumpair" :int
>        (p (:struct struct-pair) :pointer))


i wouldn't do this if it's avoidable. i as a user never mentioned
pointers, i want to be dealing with structs. and for all intents and
purposes they should behave as structs when accessing them through the
CFFI API. it's an implementation detail that on the lisp side,
internally to the CFFI implementation, they travel around as pointers
allocated into the C heap. it can (and IMO should) be completely
transparent to the user. unless i'm missing something.


> The only disadvantage of this approach is that you'll have to have
> two different functions if you will be calling the same function
> with a CL object and with a foreign pointer.


i think i'd go the opposite way: make runtime translation optional and
by default only do what i described above. for simple structs and/or
hand customized FFI's the runtime translation may come handy
sometimes, but for generated FFI's they are just bringing in
complexity, so i think it shouldn't be the default.


> I imagine that this won't happen very often; I'm guessing this is
> for chaining together foreign calls in CL, where the foreign
> structure never sees the light of day (=lisp form).


well, in my case it's the bluez API that in 99% of the cases uses
pointers for the bdaddr struct, except one single case (which is
probably only a thinko). i get all my bdaddr's from the bluez api
itself, and many of them i store for long term.

the actual example i'm stuck with:

(bind ((peer-address "00:55:44:33:22:11"))
  (with-open-hci-socket (socket :remote-device peer-address)
    (cffi:with-foreign-objects ((peer-bdaddr 'bluez:bdaddr_t)
                                (handle 'bluez:uint16_t))
      (bluez:string->bdaddr peer-address peer-bdaddr)
      (c-fun/rc bluez.ffi::hci_le_create_conn
                socket 4 4 0 bluez:+le_public_address+ peer-bdaddr
                bluez:+le_public_address+ #xf #xf 0 1000 0 0 handle 25000))))

and while we are at it, the BLUEZ:STRING->BDADDR defcfun expects a
pointer, so i would even go as far as to suggest that it's a bug that
CFFI doesn't signal an error here. i'd expect the lisp side to also
have an address-of operator and demand properly matching types.

this ties back to my recent proposal about rethinking the CFFI
API. type information should be tracked/derived at compile time and
apropriate errors signaled.

-- 
• attila lendvai
• PGP: 963F 5D5F 45C7 DFCD 0A39
--
“Foolishness is rarely a matter of lack of intelligence or even lack
of information.”
	— John McCarthy (1927–2011), father of Lisp



More information about the cffi-devel mailing list