struct to struct assignment with bitfields

80 views
Skip to first unread message

Sarvi Shanmugham

unread,
Jan 7, 2014, 7:01:44 PM1/7/14
to pytho...@googlegroups.com
I have a C structure defined as follows

struct position {
    unsigned int type:6,
             stream:14,
             newline:1,
             whitespace:1,
             pos:10;
    unsigned int line:31,
             noexpand:1;
};

struct token {
    struct position pos;
};

I have wrappers for this implemented and everything seems to compile fine.

Question:
    1. Should the following work, where tok1 and tok2 are created using ffi.new('struct token *')
     tok1.pos=tok2.pos
    2. Should the following work, swapping the values?
     tok1.pos,tok2.pos = tok2.pos,tok1.pos
    3. Should either of the above to work for structures not containing bitfields. I ask, because I understand there are some limitations with bitfields. 

After doing 2 above, I find both (tok1.pos.type, tok2.pos.type) containing the same values i.e (2,2). 
I was expecting to go from (4,2) to (2,4) for these values after executing 2 above

I am on the latest code.

Thank,
Sarvi

Armin Rigo

unread,
Jan 7, 2014, 7:21:09 PM1/7/14
to pytho...@googlegroups.com
Hi Sarvi,

On Wed, Jan 8, 2014 at 1:01 AM, Sarvi Shanmugham <sarv...@gmail.com> wrote:
> 2. Should the following work, swapping the values?
> tok1.pos,tok2.pos = tok2.pos,tok1.pos

You're falling into the same trap as someone recently did on the
LuaJIT mailing list about LuaJIT's FFI. No, this cannot work.

The issue is that reading "tok1.pos" does not create a copy of the
structure tok1.pos --- you don't want a copy, because otherwise
"tok1.pos.foo = 5" would change the value of the "foo" field in the
copy, which would then immediately be thrown away. So "tok1.pos"
returns a structure that is a *reference* to the substructure inside
"tok1". And the copying operation occurs only when executing
"tok2.pos = ...", which takes (in this case) the reference to the
substructure, and copies it into "tok2.pos". There is basically no
other way in Python.

Combine this explanation with the "tok1.pos,tok2.pos =
tok2.pos,tok1.pos" case and you'll understand why it doesn't work as
you would expect. It is the same reason for why, if "lst1" and "lst2"
are lists, then "lst1[:], lst2[:] = lst2, lst1" fails to swap the
content of the lists.

Like in C, you need three copies: p=ffi.new("struct position *");
p[0]=tok1.pos; tok1.pos=tok2.pos;tok2.pos=p[0]


A bientôt,

Armin.

Sarvi Shanmugham

unread,
Jan 8, 2014, 1:35:33 AM1/8/14
to pytho...@googlegroups.com, ar...@tunes.org
thx.
Question:
you mention that reading tok1.pos returns a reference to the substructure inside tok1.
Is there a way to create a copy of the substructure without having to do  p=ffi.new("struct position *");p[0]=tok1.pos
Looks like tok1.pos[0] is not readable, I thought I could do pos=tok1.pos[0]; tok1.pos[0]=tok2.pos[0];tok2.pos[0]=pos

Looks like tok1.pos is not a "struct position *" but a "struct position", which is why p[0]=tok1.pos works. 
Looks like need a better understanding of how substructures are handled.

Sarvi



A bientôt,

Armin.

Armin Rigo

unread,
Jan 8, 2014, 3:58:27 AM1/8/14
to pytho...@googlegroups.com
Hi Sarvi,

On Wed, Jan 8, 2014 at 7:35 AM, Sarvi Shanmugham <sarv...@gmail.com> wrote:
> Is there a way to create a copy of the substructure without having to do
> p=ffi.new("struct position *");p[0]=tok1.pos

No. That's how you need to do it. In C, you also need to declare an
extra temporary variable and make a total of three copies.

If you're too confused, we can also deprecate and eventually remove
the support for assigning to "tok1.pos". It might be the source of
the confusion, because it behaves completely differently than just
assigning to a local variable "pos". Then we could add
ffi.memcpy(dst, src[, size]) which would work on either two pointers
or two (sub)structures. The main advantage is that you could no
longer get surprizes by mixing parallel assignments with struct
assignments --- there would be only one way left to write it, the
correct one:

p = ffi.new("struct position *)
ffi.memcpy(p[0], tok1.pos)
ffi.memcpy(tok1.pos, tok2.pos)
ffi.memcpy(tok2.pos, p[0])

(I never needed it so far, but maybe ffi.memxchg()....?)


A bientôt,

Armin.

Sarvi Shanmugham

unread,
Jan 8, 2014, 4:31:40 AM1/8/14
to pytho...@googlegroups.com, ar...@tunes.org
this one feels more painful.
I guess my confusion was what tok1.pos object really was equivalent to. I thought it equivalent to the object ffi.new('struct position *')
May be because of '&' when you try to print out tok1.pos.
I now understand tok1.pos object is more equivalent to ffi.new('struct position *')[0]

That said I realize
p=ffi.new('struct position *',tok1.pos)
tok1.pos=tok2.pos
tok2.pos=p[0]
Is the cleanest and intuitive way I can see this happen. 

 In terms of API I do see a usefulness for ffi.dup().
1. I should be able to do 
p=ffi.new('struct position *',tok1.pos)
as
p=ffi.dup(tok1.pos)
Needing to use new requires the specification of "struct position*" which can be infered from tok1.pos which is a cdata itself

2. An API to swap structure contents would also be useful
ffi.swap(tok1.pos,tok2.pos)

Sarvi
Reply all
Reply to author
Forward
0 new messages