Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pointer-to-member member function overload #1021

Merged
merged 1 commit into from
Aug 7, 2023

Conversation

ZeroErrors
Copy link
Contributor

@ZeroErrors ZeroErrors commented Aug 7, 2023

Changelog

  • Added new pointer-to-member member function overload to untyped_component.
  • Added support for packed structs in flecs meta.
  • Removed unused size and alignment from EcsMetaType.
  • Fixed bug where member count was ignored if an explicit offset was provided.

Details

Pointer-to-member Member Function

This new overload simplifies adding members with offsets and also helps ensure the offset is correct even if the structure changes or padding is inserted before/after fields, it also helps deal with offsets caused by inheritance with members or virtual functions.

Using the example struct. I've included how this would usually be laid out in memory.

struct SomeData {
        char a;      // (Size: 1, Align: 1, Offset: 0)
        int b;       // (Size: 4, Align: 4, Offset: 4)
        char pad[2]; // (Size: 2, Align: 1, Offset: 8)
        double c;    // (Size: 8, Align: 8, Offset: 16)
};                   // (Size: 24, Align: 8)

You can register the component with the padding included like this:

ecs.component<SomeData>()
	.member<char>("a")
	.member<int>("b")
	.member<char>("pad", 2)
	.member<double>("c");

Or if you wanted to ignore the padding you would need to add the members with their offsets for every member.

ecs.component<SomeData>()
	.member<char>("a", 0, offsetof(SomeData, a))
	.member<int>("b", 0, offsetof(SomeData, b))
	.member<double>("c", 0, offsetof(SomeData, c));

With this change, this becomes simplified to the following:

ecs.component<SomeData>()
	.member("a", &SomeData::a)
	.member("b", &SomeData::b)
	.member("c", &SomeData::c);

The pointer-to-member overload also supports fixed-size arrays so you could register the padding like the following and it will be registered the same as doing .member<char>("pad", 2, offsetof(SomeData, pad)).

ecs.component<SomeData>()
	.member("a", &SomeData::a)
	.member("b", &SomeData::b)
	.member("pad", &SomeData::pad)
	.member("c", &SomeData::c);

Packed Structs

I've modified the case when an explicit offset is provided to use the alignment already stored in EcsComponent for the EcsStruct, this allows for the alignment of the struct to differ from the alignment of the members.

During this change I also noticed that the member count was being ignored in this case so I also updated the code to correctly calculate the size for ecs_member_t.
From this, I then calculate the expected minimum size of the struct using the member offset and the member size calculated by the size and count, this will now provide an error if a member is added with an offset and size that would exceed the size of the struct.

Because of this change, I had to adjust the test case StructTypes_partial_type_custom_offset because partial is set to true if the last member of the field is added with an offset because the expected size and actual size matches.

So with this change using the same example struct from before but this time packed. (Using MSVC)

#pragma pack(push, 1)
struct SomeData {
        char a;      // (Size: 1, Align: 1, Offset: 0)
        int b;       // (Size: 4, Align: 4, Offset: 1)
        char pad[2]; // (Size: 2, Align: 1, Offset: 5)
        double c;    // (Size: 8, Align: 8, Offset: 7)
};                   // (Size: 15, Align: 1)
#pragma pack(pop)

ecs.component<SomeData>()
	.member("a", &SomeData::a)
	.member("b", &SomeData::b)
	.member("c", &SomeData::c);

flecs::entity_to_json_desc_t desc = {
	.serialize_path = true,
	.serialize_values = true,
};
auto str = ecs.entity("e1")
	.set<SomeData>({ '-', 1, {0}, 2.1 })
	.to_json(&desc);
printf("%s\n", str.c_str());

This produces the output:

{"path":"e1", "ids":[["SomeData"]], "values":[{"a":"-", "b":1, "c":2.1}]}

Only validate alignment when generating member offset in flecs meta
@SanderMertens
Copy link
Owner

Nice! Thanks for the PR :)

@SanderMertens SanderMertens merged commit c27c1da into SanderMertens:master Aug 7, 2023
64 checks passed
@ZeroErrors ZeroErrors deleted the cpp-member-ptr branch August 7, 2023 21:42
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.

None yet

2 participants