diff --git a/flecs.c b/flecs.c index 52d5b4323..d38c9d03c 100644 --- a/flecs.c +++ b/flecs.c @@ -55556,10 +55556,6 @@ int flecs_init_type( ecs_err("computed size for '%s' matches with actual type but " "alignment is different (%d vs. %d)", ecs_get_name(world, type), alignment, comp->alignment); - } else { - /* If this is an existing type, the alignment can be larger but - * not smaller than the computed alignment. */ - alignment = comp->alignment; } } @@ -55567,8 +55563,6 @@ int flecs_init_type( } meta_type->kind = kind; - meta_type->size = size; - meta_type->alignment = alignment; ecs_modified(world, type, EcsMetaType); return 0; @@ -55714,7 +55708,7 @@ int flecs_add_member_to_struct( /* Get component of member type to get its size & alignment */ const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); if (!mbr_comp) { - char *path = ecs_get_fullpath(world, member); + char *path = ecs_get_fullpath(world, elem->type); ecs_err("member '%s' is not a type", path); ecs_os_free(path); return -1; @@ -55724,7 +55718,7 @@ int flecs_add_member_to_struct( ecs_size_t member_alignment = mbr_comp->alignment; if (!member_size || !member_alignment) { - char *path = ecs_get_fullpath(world, member); + char *path = ecs_get_fullpath(world, elem->type); ecs_err("member '%s' has 0 size/alignment"); ecs_os_free(path); return -1; @@ -55751,14 +55745,42 @@ int flecs_add_member_to_struct( } } else { /* If members have explicit offsets, we can't rely on computed - * size/alignment values. Grab size of just added member instead. It - * doesn't matter if the size doesn't correspond to the actual struct - * size. The flecs_init_type function compares computed size with actual + * size/alignment values. Calculate size as if this is the last member + * instead, since this will validate if the member fits in the struct. + * It doesn't matter if the size is smaller than the actual struct size + * because flecs_init_type function compares computed size with actual * (component) size to determine if the type is partial. */ - const EcsComponent *cptr = ecs_get(world, m->type, EcsComponent); - ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); - size = cptr->size; - alignment = cptr->alignment; + ecs_member_t *elem = &members[i]; + + ecs_assert(elem->name != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(elem->type != 0, ECS_INTERNAL_ERROR, NULL); + + /* Get component of member type to get its size & alignment */ + const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); + if (!mbr_comp) { + char *path = ecs_get_fullpath(world, elem->type); + ecs_err("member '%s' is not a type", path); + ecs_os_free(path); + return -1; + } + + ecs_size_t member_size = mbr_comp->size; + ecs_size_t member_alignment = mbr_comp->alignment; + + if (!member_size || !member_alignment) { + char *path = ecs_get_fullpath(world, elem->type); + ecs_err("member '%s' has 0 size/alignment"); + ecs_os_free(path); + return -1; + } + + member_size *= elem->count; + elem->size = member_size; + + size = elem->offset + member_size; + + const EcsComponent* comp = ecs_get(world, type, EcsComponent); + alignment = comp->alignment; } if (size == 0) { diff --git a/flecs.h b/flecs.h index 72e71e314..d0692ff1f 100644 --- a/flecs.h +++ b/flecs.h @@ -13042,8 +13042,6 @@ typedef struct EcsMetaType { ecs_type_kind_t kind; bool existing; /**< Did the type exist or is it populated from reflection */ bool partial; /**< Is the reflection data a partial type description */ - ecs_size_t size; /**< Computed size */ - ecs_size_t alignment; /**< Computed alignment */ } EcsMetaType; /** Primitive type kinds supported by meta addon */ @@ -15864,7 +15862,7 @@ struct string { return m_const_str; } - std::size_t length() { + std::size_t length() const { return static_cast(m_length); } @@ -15873,7 +15871,7 @@ struct string { return N - 1; } - std::size_t size() { + std::size_t size() const { return length(); } @@ -24733,25 +24731,6 @@ struct untyped_component : entity { * @{ */ -/** Add member. */ -untyped_component& member(flecs::entity_t type_id, const char *name, int32_t count = 0, size_t offset = 0) { - ecs_entity_desc_t desc = {}; - desc.name = name; - desc.add[0] = ecs_pair(flecs::ChildOf, m_id); - ecs_entity_t eid = ecs_entity_init(m_world, &desc); - ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); - - flecs::entity e(m_world, eid); - - Member m = {}; - m.type = type_id; - m.count = count; - m.offset = static_cast(offset); - e.set(m); - - return *this; -} - /** Add member with unit. */ untyped_component& member(flecs::entity_t type_id, flecs::entity_t unit, const char *name, int32_t count = 0, size_t offset = 0) { ecs_entity_desc_t desc = {}; @@ -24772,6 +24751,11 @@ untyped_component& member(flecs::entity_t type_id, flecs::entity_t unit, const c return *this; } +/** Add member. */ +untyped_component& member(flecs::entity_t type_id, const char* name, int32_t count = 0, size_t offset = 0) { + return member(type_id, 0, name, count, offset); +} + /** Add member. */ template untyped_component& member(const char *name, int32_t count = 0, size_t offset = 0) { @@ -24794,6 +24778,31 @@ untyped_component& member(const char *name, int32_t count = 0, size_t offset = 0 return member(type_id, unit_id, name, count, offset); } +/** Add member using pointer-to-member. */ +template ::type> +untyped_component& member(const char* name, const MemberType ComponentType::* ptr) { + flecs::entity_t type_id = _::cpp_type::id(m_world); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, name, std::extent::value, offset); +} + +/** Add member with unit using pointer-to-member. */ +template ::type> +untyped_component& member(flecs::entity_t unit, const char* name, const MemberType ComponentType::* ptr) { + flecs::entity_t type_id = _::cpp_type::id(m_world); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, unit, name, std::extent::value, offset); +} + +/** Add member with unit using pointer-to-member. */ +template ::type> +untyped_component& member(const char* name, const MemberType ComponentType::* ptr) { + flecs::entity_t type_id = _::cpp_type::id(m_world); + flecs::entity_t unit_id = _::cpp_type::id(m_world); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, unit_id, name, std::extent::value, offset); +} + /** Add constant. */ untyped_component& constant(const char *name, int32_t value) { ecs_add_id(m_world, m_id, _::cpp_type::id(m_world)); diff --git a/include/flecs/addons/cpp/mixins/meta/untyped_component.inl b/include/flecs/addons/cpp/mixins/meta/untyped_component.inl index 5fbf40e31..12c57b1db 100644 --- a/include/flecs/addons/cpp/mixins/meta/untyped_component.inl +++ b/include/flecs/addons/cpp/mixins/meta/untyped_component.inl @@ -10,25 +10,6 @@ * @{ */ -/** Add member. */ -untyped_component& member(flecs::entity_t type_id, const char *name, int32_t count = 0, size_t offset = 0) { - ecs_entity_desc_t desc = {}; - desc.name = name; - desc.add[0] = ecs_pair(flecs::ChildOf, m_id); - ecs_entity_t eid = ecs_entity_init(m_world, &desc); - ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL); - - flecs::entity e(m_world, eid); - - Member m = {}; - m.type = type_id; - m.count = count; - m.offset = static_cast(offset); - e.set(m); - - return *this; -} - /** Add member with unit. */ untyped_component& member(flecs::entity_t type_id, flecs::entity_t unit, const char *name, int32_t count = 0, size_t offset = 0) { ecs_entity_desc_t desc = {}; @@ -49,6 +30,11 @@ untyped_component& member(flecs::entity_t type_id, flecs::entity_t unit, const c return *this; } +/** Add member. */ +untyped_component& member(flecs::entity_t type_id, const char* name, int32_t count = 0, size_t offset = 0) { + return member(type_id, 0, name, count, offset); +} + /** Add member. */ template untyped_component& member(const char *name, int32_t count = 0, size_t offset = 0) { @@ -71,6 +57,31 @@ untyped_component& member(const char *name, int32_t count = 0, size_t offset = 0 return member(type_id, unit_id, name, count, offset); } +/** Add member using pointer-to-member. */ +template ::type> +untyped_component& member(const char* name, const MemberType ComponentType::* ptr) { + flecs::entity_t type_id = _::cpp_type::id(m_world); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, name, std::extent::value, offset); +} + +/** Add member with unit using pointer-to-member. */ +template ::type> +untyped_component& member(flecs::entity_t unit, const char* name, const MemberType ComponentType::* ptr) { + flecs::entity_t type_id = _::cpp_type::id(m_world); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, unit, name, std::extent::value, offset); +} + +/** Add member with unit using pointer-to-member. */ +template ::type> +untyped_component& member(const char* name, const MemberType ComponentType::* ptr) { + flecs::entity_t type_id = _::cpp_type::id(m_world); + flecs::entity_t unit_id = _::cpp_type::id(m_world); + size_t offset = reinterpret_cast(&(static_cast(nullptr)->*ptr)); + return member(type_id, unit_id, name, std::extent::value, offset); +} + /** Add constant. */ untyped_component& constant(const char *name, int32_t value) { ecs_add_id(m_world, m_id, _::cpp_type::id(m_world)); diff --git a/include/flecs/addons/meta.h b/include/flecs/addons/meta.h index 24e1d7cfc..07ad67c7c 100644 --- a/include/flecs/addons/meta.h +++ b/include/flecs/addons/meta.h @@ -154,8 +154,6 @@ typedef struct EcsMetaType { ecs_type_kind_t kind; bool existing; /**< Did the type exist or is it populated from reflection */ bool partial; /**< Is the reflection data a partial type description */ - ecs_size_t size; /**< Computed size */ - ecs_size_t alignment; /**< Computed alignment */ } EcsMetaType; /** Primitive type kinds supported by meta addon */ diff --git a/src/addons/meta/meta.c b/src/addons/meta/meta.c index 580c50276..0422cea3c 100644 --- a/src/addons/meta/meta.c +++ b/src/addons/meta/meta.c @@ -299,10 +299,6 @@ int flecs_init_type( ecs_err("computed size for '%s' matches with actual type but " "alignment is different (%d vs. %d)", ecs_get_name(world, type), alignment, comp->alignment); - } else { - /* If this is an existing type, the alignment can be larger but - * not smaller than the computed alignment. */ - alignment = comp->alignment; } } @@ -310,8 +306,6 @@ int flecs_init_type( } meta_type->kind = kind; - meta_type->size = size; - meta_type->alignment = alignment; ecs_modified(world, type, EcsMetaType); return 0; @@ -457,7 +451,7 @@ int flecs_add_member_to_struct( /* Get component of member type to get its size & alignment */ const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); if (!mbr_comp) { - char *path = ecs_get_fullpath(world, member); + char *path = ecs_get_fullpath(world, elem->type); ecs_err("member '%s' is not a type", path); ecs_os_free(path); return -1; @@ -467,7 +461,7 @@ int flecs_add_member_to_struct( ecs_size_t member_alignment = mbr_comp->alignment; if (!member_size || !member_alignment) { - char *path = ecs_get_fullpath(world, member); + char *path = ecs_get_fullpath(world, elem->type); ecs_err("member '%s' has 0 size/alignment"); ecs_os_free(path); return -1; @@ -494,14 +488,42 @@ int flecs_add_member_to_struct( } } else { /* If members have explicit offsets, we can't rely on computed - * size/alignment values. Grab size of just added member instead. It - * doesn't matter if the size doesn't correspond to the actual struct - * size. The flecs_init_type function compares computed size with actual + * size/alignment values. Calculate size as if this is the last member + * instead, since this will validate if the member fits in the struct. + * It doesn't matter if the size is smaller than the actual struct size + * because flecs_init_type function compares computed size with actual * (component) size to determine if the type is partial. */ - const EcsComponent *cptr = ecs_get(world, m->type, EcsComponent); - ecs_assert(cptr != NULL, ECS_INTERNAL_ERROR, NULL); - size = cptr->size; - alignment = cptr->alignment; + ecs_member_t *elem = &members[i]; + + ecs_assert(elem->name != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(elem->type != 0, ECS_INTERNAL_ERROR, NULL); + + /* Get component of member type to get its size & alignment */ + const EcsComponent *mbr_comp = ecs_get(world, elem->type, EcsComponent); + if (!mbr_comp) { + char *path = ecs_get_fullpath(world, elem->type); + ecs_err("member '%s' is not a type", path); + ecs_os_free(path); + return -1; + } + + ecs_size_t member_size = mbr_comp->size; + ecs_size_t member_alignment = mbr_comp->alignment; + + if (!member_size || !member_alignment) { + char *path = ecs_get_fullpath(world, elem->type); + ecs_err("member '%s' has 0 size/alignment"); + ecs_os_free(path); + return -1; + } + + member_size *= elem->count; + elem->size = member_size; + + size = elem->offset + member_size; + + const EcsComponent* comp = ecs_get(world, type, EcsComponent); + alignment = comp->alignment; } if (size == 0) { diff --git a/test/cpp_api/project.json b/test/cpp_api/project.json index 182f8a408..7bc22e394 100644 --- a/test/cpp_api/project.json +++ b/test/cpp_api/project.json @@ -1267,7 +1267,9 @@ "enum_w_bits", "value_range", "warning_range", - "error_range" + "error_range", + "struct_member_ptr", + "struct_member_ptr_packed_struct" ] }, { "id": "Table", diff --git a/test/cpp_api/src/Meta.cpp b/test/cpp_api/src/Meta.cpp index 1d16e8736..be9eac4b6 100644 --- a/test/cpp_api/src/Meta.cpp +++ b/test/cpp_api/src/Meta.cpp @@ -1117,3 +1117,126 @@ void Meta_error_range() { test_assert(ranges->error.max == 2); } } + +void Meta_struct_member_ptr() { + flecs::world ecs; + + struct Test { + int32_t x; + }; + struct Test2 { + double y; + }; + struct Nested { + Test a; + int pad; // This will be ignored + Test2 b[2]; + }; + + auto t = ecs.component() + .member("x", &Test::x); + + auto t2 = ecs.component() + .member("y", &Test2::y); + + auto n = ecs.component() + .member("a", &Nested::a) + .member("b", &Nested::b); + + { // Validate Test + test_assert(t != 0); + + auto x = t.lookup("x"); + test_assert(x != 0); + test_assert(x.has()); + const flecs::Member *xm = x.get(); + test_uint(xm->type, flecs::I32); + test_uint(xm->offset, offsetof(Test, x)); + } + { // Validate Test2 + test_assert(t2 != 0); + + auto y = t2.lookup("y"); + test_assert(y != 0); + test_assert(y.has()); + const flecs::Member *ym = y.get(); + test_uint(ym->type, flecs::F64); + test_uint(ym->offset, offsetof(Test2, y)); + } + + // Validate Nested + test_assert(n != 0); + { + auto a = n.lookup("a"); + test_assert(a != 0); + test_assert(a.has()); + const flecs::Member *am = a.get(); + test_uint(am->type, t); + test_uint(am->offset, offsetof(Nested, a)); + } + { + auto b = n.lookup("b"); + test_assert(b != 0); + test_assert(b.has()); + const flecs::Member *bm = b.get(); + test_uint(bm->type, t2); + test_uint(bm->offset, offsetof(Nested, b)); + test_uint(bm->count, 2); + } +} + +void Meta_struct_member_ptr_packed_struct() { + flecs::world ecs; + +#if defined(__GNUC__) || defined(__clang__) + struct PackedStruct { + char a; + int b; + char pad[2]; + double c; + } __attribute__((__packed__)); +#endif + +#if defined(_MSC_VER) +#pragma pack(push, 1) + struct PackedStruct { + char a; + int b; + char pad[2]; + double c; + }; +#pragma pack(pop) +#endif + + auto s = ecs.component() + .member("a", &PackedStruct::a) + .member("b", &PackedStruct::b) + .member("c", &PackedStruct::c); + + test_assert(s != 0); + + { + auto a = s.lookup("a"); + test_assert(a != 0); + test_assert(a.has()); + const flecs::Member *am = a.get(); + test_uint(am->type, flecs::Char); + test_uint(am->offset, offsetof(PackedStruct, a)); + } + { + auto b = s.lookup("b"); + test_assert(b != 0); + test_assert(b.has()); + const flecs::Member *bm = b.get(); + test_uint(bm->type, flecs::I32); + test_uint(bm->offset, offsetof(PackedStruct, b)); + } + { + auto c = s.lookup("c"); + test_assert(c != 0); + test_assert(c.has()); + const flecs::Member *cm = c.get(); + test_uint(cm->type, flecs::F64); + test_uint(cm->offset, offsetof(PackedStruct, c)); + } +} diff --git a/test/cpp_api/src/main.cpp b/test/cpp_api/src/main.cpp index e1e5641b8..dbe45f66d 100644 --- a/test/cpp_api/src/main.cpp +++ b/test/cpp_api/src/main.cpp @@ -1212,6 +1212,8 @@ void Meta_enum_w_bits(void); void Meta_value_range(void); void Meta_warning_range(void); void Meta_error_range(void); +void Meta_struct_member_ptr(void); +void Meta_struct_member_ptr_packed_struct(void); // Testsuite 'Table' void Table_each(void); @@ -5936,6 +5938,14 @@ bake_test_case Meta_testcases[] = { { "error_range", Meta_error_range + }, + { + "struct_member_ptr", + Meta_struct_member_ptr + }, + { + "struct_member_ptr_packed_struct", + Meta_struct_member_ptr_packed_struct } }; @@ -6269,7 +6279,7 @@ static bake_test_suite suites[] = { "Meta", NULL, NULL, - 45, + 47, Meta_testcases }, { diff --git a/test/meta/include/meta.h b/test/meta/include/meta.h index eb2a6138a..bfb883dfc 100644 --- a/test/meta/include/meta.h +++ b/test/meta/include/meta.h @@ -55,6 +55,10 @@ typedef struct { Point start, stop; } Line; +typedef struct { + int32_t x, y, z; +} Vec3; + #ifdef __cplusplus } #endif diff --git a/test/meta/src/StructTypes.c b/test/meta/src/StructTypes.c index ef2353cb6..fcd554c99 100644 --- a/test/meta/src/StructTypes.c +++ b/test/meta/src/StructTypes.c @@ -402,7 +402,7 @@ void StructTypes_partial_type() { ecs_entity_t s = ecs_struct_init(world, &(ecs_struct_desc_t){ .entity = ecs_id(Position), - .members = {{ .name = "x", .type = ecs_id(ecs_f32_t) }} + .members = {{ .name = "x", .type = ecs_id(ecs_i32_t) }} }); test_assert(s == ecs_id(Position)); @@ -418,7 +418,7 @@ void StructTypes_partial_type() { test_bool(mptr->existing, true); meta_test_struct(world, s, Position); - meta_test_member(world, s, Position, x, ecs_id(ecs_f32_t), 1); + meta_test_member(world, s, Position, x, ecs_id(ecs_i32_t), 1); ecs_fini(world); } @@ -426,31 +426,31 @@ void StructTypes_partial_type() { void StructTypes_partial_type_custom_offset() { ecs_world_t *world = ecs_init(); - ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Vec3); ecs_entity_t s = ecs_struct_init(world, &(ecs_struct_desc_t){ - .entity = ecs_id(Position), + .entity = ecs_id(Vec3), .members = {{ - .name = "y", - .type = ecs_id(ecs_f32_t), - .offset = offsetof(Position, y) + .name = "y", + .type = ecs_id(ecs_i32_t), + .offset = offsetof(Vec3, y) }} }); - test_assert(s == ecs_id(Position)); + test_assert(s == ecs_id(Vec3)); const EcsComponent *cptr = ecs_get(world, s, EcsComponent); test_assert(cptr != NULL); - test_int(cptr->size, sizeof(Position)); - test_int(cptr->alignment, ECS_ALIGNOF(Position)); + test_int(cptr->size, sizeof(Vec3)); + test_int(cptr->alignment, ECS_ALIGNOF(Vec3)); const EcsMetaType *mptr = ecs_get(world, s, EcsMetaType); test_assert(mptr != NULL); test_bool(mptr->partial, true); test_bool(mptr->existing, true); - meta_test_struct(world, s, Position); - meta_test_member(world, s, Position, y, ecs_id(ecs_f32_t), 1); + meta_test_struct(world, s, Vec3); + meta_test_member(world, s, Vec3, y, ecs_id(ecs_i32_t), 1); ecs_fini(world); } @@ -817,8 +817,6 @@ void StructTypes_struct_w_16_alignment() { test_assert(mptr != NULL); test_bool(mptr->partial, false); test_bool(mptr->existing, true); - test_int(mptr->size, sizeof(T)); - test_int(mptr->alignment, 16); ecs_fini(world); }