Consider the following definitions
(defcstruct record1 (a :int) (b :int)) (defctype record1-type (:struct record1)) (defcstruct record2a (r (:struct record1)) (c :int)) (defcstruct record2b (r record1-type) (c :int))
record2a_type and record2b_type have identical representations. However when translating structs using libffi-fsvb record2b_type will put a pointer in the r field rather than translating the typedef:
struct record2a foo_a() { return (struct record2a) { .r = { .a = 1, .b = 2 }, .c = 2 }; }
struct record2a foo_b() { return (struct record2a) { .r = { .a = 1, .b = 2 }, .c = 2 }; }
(setq $a (cffi:foreign-funcall "foo_a" (:struct record2a))) ;; => (R (B 2 A 1) C 2) (setq $b (cffi:foreign-funcall "foo_b" (:struct record2b))) ;; => (R #<foreign :VOID 0x1566e70> C 2)
The pointer in the r field is useless, because of its extent.
The immediate fix for this is to define (either in structures or early-types)
(defmethod translate-from-foreign (value (type foreign-typedef)) (translate-from-foreign value (follow-typedefs)))
so foreign-typedefs always resolve to a structure
But I am not sure. Does this have other ramifications: there seems to be confusion over how typedefs are resolved (and I do not understand code for the bare structs and enhanced-typedefs - perhaps I am expected to go about this problem in some other way? Am I expected to define methods on translate-from-foreign-memory? or should CFFI always translate nested aggregates to lisp lists)
On Fri, 14 Feb 2020 at 10:41, Madhu enometh@meer.net wrote:
But I am not sure. Does this have other ramifications: there seems to be confusion over how typedefs are resolved (and I do not understand code for the bare structs and enhanced-typedefs - perhaps I am expected to go about this problem in some other way? Am I expected to define methods on translate-from-foreign-memory? or should CFFI always translate nested aggregates to lisp lists)
I can't check right now, but I wonder if the TYPEP check in DEFCTYPE should be checking for TRANSLATABLE-FOREIGN-TYPE instead of ENHANCED-FOREIGN-TYPE?
* Luís Oliveira Wrote on Fri, 14 Feb 2020 11:24:08 +0000
I can't check right now, but I wonder if the TYPEP check in DEFCTYPE should be checking for TRANSLATABLE-FOREIGN-TYPE instead of ENHANCED-FOREIGN-TYPE?
Yes, making that change also fixes the problem. Impressive. Thanks,
Thanks for testing. Could you send a pull request?
On Sat, Feb 15, 2020, 1:40 PM Madhu enometh@meer.net wrote:
- Luís Oliveira Wrote on Fri, 14 Feb 2020 11:24:08 +0000
I can't check right now, but I wonder if the TYPEP check in DEFCTYPE should be checking for TRANSLATABLE-FOREIGN-TYPE instead of ENHANCED-FOREIGN-TYPE?
Yes, making that change also fixes the problem. Impressive. Thanks,
* Luís Oliveira CAB-HnLSGQUdZzNc_KN6nZkMvMMp-qOxR_OfHTuC-y7jfe39FPA@mail.gmail.com Wrote on Sat, 15 Feb 2020 15:35:00 +0000
Thanks for testing. Could you send a pull request?
[Sorry for the delay - I was waiting to figure out how to use github v4 to clone the repository into my space so I could submit a PR. I finally figured out I couldn't]
Unfortunately I didn't run the tests before I sent my message. Now that I did run them I notice that proposed fix introduces 9 unexpected failures: STRUCT.NESTED-SETF, STRUCT.ALIGNMENT.1-8
struct s_s_ch the_s_s_ch = { 2, { 1 } }; (defcvar "the_s_s_ch" s-s-ch) -> *s-s-ch* gets translated to a list.
So fixing DEFCTYPE probably wouldn't cut it.
The earlier proposal of a TRANSLATE-FROM-FOREIGN method on FOREIGN-TYPEDEF works. But the questions I had regardingthat are still unanswered.
Here is is a test in structs.lisp for to test that:
;; Test if a field defined by a typedef is translated by default to a ;; lisp object (defcstruct struct-pair-plus-one-a (p (:struct struct-pair)) (c :int))
(defcstruct struct-pair-plus-one-b (p struct-pair-typedef1) (c :int))
(defcfun ("make_pair_plus_one" make-pair-plus-one-a) (:struct struct-pair-plus-one-a) (a :int) (b :int) (c :int))
(defcfun ("make_pair_plus_one" make-pair-plus-one-b) (:struct struct-pair-plus-one-b) (a :int) (b :int) (c :int))
(deftest struct-values.fsbv.1 (let ((a (make-pair-plus-one-a 1 2 3))) (values (getf a 'p) (getf a 'c))) (1 . 2) 3)
(deftest struct-values.fsbv.2 (let ((b (make-pair-plus-one-b 1 2 3))) (values (getf b 'p) (getf b 'c))) (1 . 2) 3)
Let me know what you think
On Sat, Feb 15, 2020, 1:40 PM Madhu enometh@meer.net wrote:
- Luís Oliveira Wrote on Fri, 14 Feb 2020 11:24:08 +0000
I can't check right now, but I wonder if the TYPEP check in DEFCTYPE should be checking for TRANSLATABLE-FOREIGN-TYPE instead of ENHANCED-FOREIGN-TYPE?
Yes, making that change also fixes the problem. Impressive. Thanks,
On Thu, 20 Feb 2020 at 09:31, Madhu enometh@meer.net wrote:
struct s_s_ch the_s_s_ch = { 2, { 1 } }; (defcvar "the_s_s_ch" s-s-ch) -> *s-s-ch* gets translated to a list.
And perhaps it should, why not? That seems like an oversight.
Luís
* Luís Oliveira luismbo@gmail.com CAB-HnLS=k2wUnomOZ-U4Sov6OfTG4zJkc+mQAhnPBAqWdYXSyw@mail.gmail.com Wrote on Thu, 20 Feb 2020 13:28:45 +0000
On Thu, 20 Feb 2020 at 09:31, Madhu enometh@meer.net wrote:
struct s_s_ch the_s_s_ch = { 2, { 1 } }; (defcvar "the_s_s_ch" s-s-ch) -> *s-s-ch* gets translated to a list.
And perhaps it should, why not?
Because unlike aggregate struct values which are passed on the stack, such a variable generally represents a mutable memory location.
defcvar would no longer define a variable that reflects access to the object on the C side.
so instead of (setf (foreign-slot-value *s-s-ch* 's-s-ch 'another-char) #\a) one would have to do (setf (foreign-slot-value (foreign-symbol-pointer "the_s_s_ch") 's-s-ch 'another-char) #\a)
This pretty painful when the nesting gets deep. I started trying to convert the tests and I stopped around struct.alignment.4
On Thu, 20 Feb 2020 at 15:09, Madhu enometh@meer.net wrote:
defcvar would no longer define a variable that reflects access to the object on the C side.
so instead of (setf (foreign-slot-value *s-s-ch* 's-s-ch 'another-char) #\a) one would have to do (setf (foreign-slot-value (foreign-symbol-pointer "the_s_s_ch") 's-s-ch 'another-char) #\a)
This pretty painful when the nesting gets deep. I started trying to convert the tests and I stopped around struct.alignment.4
There's (get-var-pointer '*s-s-ch*) but it's not much better, I agree. We could have DEFCVAR define a symbol macro for &*S-S-CH* that would get the pointer, maybe?
For nesting, one can use FOREIGN-SLOT-POINTER explicitly or the (:pointer foo) for WITH-FOREIGN-SLOTS.
What do you think?
* Luís Oliveira luismbo@gmail.com CAB-HnLR-ajjUvzjoF08kAsLVWC4xEyCPOn__C8fT36XO_sX8gQ@mail.gmail.com Wrote on Thu, 20 Feb 2020 20:09:20 +0000
On Thu, 20 Feb 2020 at 15:09, Madhu enometh@meer.net wrote:
This pretty painful when the nesting gets deep. I started trying to convert the tests and I stopped around struct.alignment.4
There's (get-var-pointer '*s-s-ch*) but it's not much better, I agree. We could have DEFCVAR define a symbol macro for &*S-S-CH* that would get the pointer, maybe?
For nesting, one can use FOREIGN-SLOT-POINTER explicitly or the (:pointer foo) for WITH-FOREIGN-SLOTS.
What do you think?
I've gone ahead and submitted a PR so you can see how the changes look. better to decide later,