Skip to content

Conversation

@sinisterchipmunk
Copy link
Contributor

This pull request was split from #14 as requested by @nobu . It depends on #26 because without that a struct which contains an array of structs would not work properly.

This pull request adds support for nested structs, unions containing structs, and nested arrays of structs/unions, to arbitrary depth.

Here are some examples:

StudentCollegeDetail = struct [
  'int college_id',
  'char college_name[50]'
]

StudentDetail = struct [
  'int id',
  'char name[20]',
  { clg_data: StudentCollegeDetail }
]

It is also possible to specify the nested struct in-line, by replacing StudentCollegeDetail with struct([...]), like so:

StudentDetail = struct [
  'int id',
  'char name[20]',
  {
    clg_data: struct([
                      'int college_id',
                      'char college_name[50]'
                    ])
  }
]

Additionally, an array of structs within a parent struct can be specified like so:

Rect = struct [{"offsets[2]" => struct(["int x", "int y"]) }]
rect = Rect.malloc
# these fields exist because of the "[2]" in "offsets[2]"
rect.offsets[0].x
rect.offsets[0].y
rect.offsets[1].x
rect.offsets[1].y

The position of the hash in the list of members determines the offset into the parent struct at which the nested struct appears. For example:

struct(['int i', { clg_data: struct(['int j']) }])
#=> int i has offset 0 bytes, clg_data.j has offset 4 bytes

struct([{ clg_data: struct(['int j']) }, 'int i'])
#=> clg_data.j has offset 0 bytes, int i has offset 4 bytes

There can be multiple hashes, or a single hash with multiple keys/values, or any combination of these. Since Ruby maintains hash order, the offsets will be as expected. The following examples are also valid syntax, but will result in the struct members being laid out at different offsets within the outer struct's memory:

# order of members in memory: position, id, dimensions
Rect = struct [ { position: struct(['float x', 'float y']) },
                'int id',
                { dimensions: struct(['float w', 'float h']) }
              ]

# order of members in memory: id, position, dimensions (assuming ordered hash)
Rect = struct [ 'int id',
                {
                  position: struct(['float x', 'float y']),
                  dimensions: struct(['float w', 'float h'])
                }
              ]

case ty[0]
when TYPE_VOIDP
val = val.collect{|v| Pointer.new(v)}
val = val.collect{|v| v = Pointer.new(v); v.size = SIZEOF_VOIDP; v }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pointer#size is not the size of address. It's the size of area that is pointed by the address.

@kou kou changed the title Add support for nested structs (split) Add support for nested structs Sep 12, 2020
@kou
Copy link
Member

kou commented Sep 12, 2020

Thanks!
Sorry for late review.

And sorry for changing implementation details without asking you.
I keep most of your API and concept but change some implementation details:

  • Remove memcpy because we have pointer[offset, length] = value for memcpy
  • Remove offset_of because it's not used
  • Use CStruct for struct signature instead of [[MEMBER_TYPE0, MEMBER_TYPE1, ...], COUNT, ENTITY_CLASS]
  • Set nested struct value by setting each member value instead of setting all members by memcpy

We can restore your change from your commits in this branch.

I'll merge this. If you find any problems, we can work on them as follow-up tasks.

@kou kou merged commit 08e0998 into ruby:master Sep 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants