Skip to content

Commit

Permalink
const string opaque type implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
jpeletier authored and SanderMertens committed Aug 24, 2024
1 parent 4d506fe commit 20ce9b3
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 6 deletions.
34 changes: 32 additions & 2 deletions distr/flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -48537,6 +48537,13 @@ size_t flecs_addon_vec_count(const void *ptr) {
return flecs_ito(size_t, count);
}

static
int flecs_const_str_serialize(const ecs_serializer_t *ser, const void *ptr) {
char **data = ECS_CONST_CAST(char**, ptr);
ser->value(ser, ecs_id(ecs_string_t), data);
return 0;
}

/* Initialize reflection data for core components */
static
void flecs_meta_import_core_definitions(
Expand All @@ -48557,6 +48564,29 @@ void flecs_meta_import_core_definitions(
}
});

/* Define const string as an opaque type that maps to string
This enables reflection for strings that are in .rodata,
(read-only) so that the meta add-on does not try to free them.
This opaque type defines how to serialize (read) the string,
but won't let users assign a new value.
*/
ecs_entity_t const_string = ecs_opaque(world, {
.entity = ecs_component(world, {
.entity = ecs_entity(world, {
.name = "flecs.core.const_string_t",
.root_sep = ""
}),
.type = {
.size = ECS_SIZEOF(const char*),
.alignment = ECS_ALIGNOF(const char*)
}
}),
.type = {
.as_type = ecs_id(ecs_string_t),
.serialize = flecs_const_str_serialize,
}
});

ecs_entity_t string_vec = ecs_vector(world, {
.entity = ecs_entity(world, {
.name = "flecs.core.string_vec_t",
Expand Down Expand Up @@ -48589,9 +48619,9 @@ void flecs_meta_import_core_definitions(
.root_sep = ""
}),
.members = {
{ .name = "compiler", .type = ecs_id(ecs_string_t) },
{ .name = "compiler", .type = const_string },
{ .name = "addons", .type = addon_vec },
{ .name = "version", .type = ecs_id(ecs_string_t) },
{ .name = "version", .type = const_string },
{ .name = "version_major", .type = ecs_id(ecs_i16_t) },
{ .name = "version_minor", .type = ecs_id(ecs_i16_t) },
{ .name = "version_patch", .type = ecs_id(ecs_i16_t) },
Expand Down
34 changes: 32 additions & 2 deletions src/addons/meta/definitions.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ size_t flecs_addon_vec_count(const void *ptr) {
return flecs_ito(size_t, count);
}

static
int flecs_const_str_serialize(const ecs_serializer_t *ser, const void *ptr) {
char **data = ECS_CONST_CAST(char**, ptr);
ser->value(ser, ecs_id(ecs_string_t), data);
return 0;
}

/* Initialize reflection data for core components */
static
void flecs_meta_import_core_definitions(
Expand All @@ -50,6 +57,29 @@ void flecs_meta_import_core_definitions(
}
});

/* Define const string as an opaque type that maps to string
This enables reflection for strings that are in .rodata,
(read-only) so that the meta add-on does not try to free them.
This opaque type defines how to serialize (read) the string,
but won't let users assign a new value.
*/
ecs_entity_t const_string = ecs_opaque(world, {
.entity = ecs_component(world, {
.entity = ecs_entity(world, {
.name = "flecs.core.const_string_t",
.root_sep = ""
}),
.type = {
.size = ECS_SIZEOF(const char*),
.alignment = ECS_ALIGNOF(const char*)
}
}),
.type = {
.as_type = ecs_id(ecs_string_t),
.serialize = flecs_const_str_serialize,
}
});

ecs_entity_t string_vec = ecs_vector(world, {
.entity = ecs_entity(world, {
.name = "flecs.core.string_vec_t",
Expand Down Expand Up @@ -82,9 +112,9 @@ void flecs_meta_import_core_definitions(
.root_sep = ""
}),
.members = {
{ .name = "compiler", .type = ecs_id(ecs_string_t) },
{ .name = "compiler", .type = const_string },
{ .name = "addons", .type = addon_vec },
{ .name = "version", .type = ecs_id(ecs_string_t) },
{ .name = "version", .type = const_string },
{ .name = "version_major", .type = ecs_id(ecs_i16_t) },
{ .name = "version_minor", .type = ecs_id(ecs_i16_t) },
{ .name = "version_patch", .type = ecs_id(ecs_i16_t) },
Expand Down
3 changes: 2 additions & 1 deletion test/meta/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,8 @@
"deser_entity_from_json",
"ser_deser_world_w_ser_opaque",
"ser_deser_entity",
"ser_deser_0_entity"
"ser_deser_0_entity",
"const_string"
]
}, {
"id": "Misc",
Expand Down
48 changes: 48 additions & 0 deletions test/meta/src/OpaqueTypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,51 @@ void OpaqueTypes_ser_deser_0_entity(void) {

ecs_fini(world);
}


void OpaqueTypes_const_string(void) {

typedef struct test_string_struct {
const char *value;
} test_string_struct;

const char *test_string = "Const String. Don't try to free me!";

ecs_world_t *world = ecs_init();

// Lookup const string type:
ecs_entity_t const_string = ecs_lookup(world, "flecs.core.const_string_t");

// Create a reflection interface for `test_struct`:
ecs_entity_t test_struct = ecs_struct(world, {
.entity = ecs_entity(world, {
.name = "test_struct", .root_sep = ""}),
.members = {
{
.name = "value",
.type = const_string },
}
});

ecs_entity_t e = ecs_entity(world, {.name = "MyEntity"});
test_string_struct *st = (test_string_struct *) ecs_ensure_id(world, e, test_struct);
st->value = test_string;

// we should be able to retrieve the string with a cursor:
ecs_meta_cursor_t cursor = ecs_meta_cursor(world, test_struct, st);
ecs_meta_push(&cursor);
ecs_meta_member(&cursor, "value");

const char *retrieved_string = ecs_meta_get_string(&cursor);

test_assert(!ecs_os_strcmp(retrieved_string, test_string));

// When deleting the entity, Flecs must not try to free the above const string,
// otherwise the below will segfault:
ecs_delete(world, e);

test_assert(true); // If we get to this point without a crash, we're good.

ecs_fini(world);

}
7 changes: 6 additions & 1 deletion test/meta/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,7 @@ void OpaqueTypes_deser_entity_from_json(void);
void OpaqueTypes_ser_deser_world_w_ser_opaque(void);
void OpaqueTypes_ser_deser_entity(void);
void OpaqueTypes_ser_deser_0_entity(void);
void OpaqueTypes_const_string(void);

// Testsuite 'Misc'
void Misc_primitive_from_stage(void);
Expand Down Expand Up @@ -4407,6 +4408,10 @@ bake_test_case OpaqueTypes_testcases[] = {
{
"ser_deser_0_entity",
OpaqueTypes_ser_deser_0_entity
},
{
"const_string",
OpaqueTypes_const_string
}
};

Expand Down Expand Up @@ -4705,7 +4710,7 @@ static bake_test_suite suites[] = {
"OpaqueTypes",
NULL,
NULL,
17,
18,
OpaqueTypes_testcases
},
{
Expand Down

0 comments on commit 20ce9b3

Please sign in to comment.