Skip to content

Commit

Permalink
#696 Custom underlying enum type
Browse files Browse the repository at this point in the history
* #696 Add support for enums with customizable underlying types

* #696 Add support for customizable underlying enum types for C++
  • Loading branch information
SanderMertens authored Dec 19, 2024
1 parent b11d241 commit 0bf9948
Show file tree
Hide file tree
Showing 19 changed files with 1,990 additions and 406 deletions.
442 changes: 318 additions & 124 deletions distr/flecs.c

Large diffs are not rendered by default.

70 changes: 55 additions & 15 deletions distr/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -15659,14 +15659,19 @@ typedef struct ecs_enum_constant_t {
const char *name;

/** May be set when used with ecs_enum_desc_t */
int32_t value;
int64_t value;

/** For when the underlying type is unsigned */
uint64_t value_unsigned;

/** Should not be set by ecs_enum_desc_t */
ecs_entity_t constant;
} ecs_enum_constant_t;

/** Component added to enum type entities */
typedef struct EcsEnum {
ecs_entity_t underlying_type;

/** Populated from child entities with Constant component */
ecs_map_t constants; /**< map<i32_t, ecs_enum_constant_t> */
} EcsEnum;
Expand All @@ -15677,7 +15682,10 @@ typedef struct ecs_bitmask_constant_t {
const char *name;

/** May be set when used with ecs_bitmask_desc_t */
ecs_flags32_t value;
ecs_flags64_t value;

/** Keep layout the same with ecs_enum_constant_t */
int64_t _unused;

/** Should not be set by ecs_bitmask_desc_t */
ecs_entity_t constant;
Expand Down Expand Up @@ -16346,6 +16354,7 @@ ecs_entity_t ecs_primitive_init(
typedef struct ecs_enum_desc_t {
ecs_entity_t entity; /**< Existing entity to use for type (optional). */
ecs_enum_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Enum constants. */
ecs_entity_t underlying_type;
} ecs_enum_desc_t;

/** Create a new enum type.
Expand Down Expand Up @@ -17038,15 +17047,18 @@ ecs_entity_t ecs_cpp_component_register(
FLECS_API
void ecs_cpp_enum_init(
ecs_world_t *world,
ecs_entity_t id);
ecs_entity_t id,
ecs_entity_t underlying_type);

FLECS_API
ecs_entity_t ecs_cpp_enum_constant_register(
ecs_world_t *world,
ecs_entity_t parent,
ecs_entity_t id,
const char *name,
int value);
void *value,
ecs_entity_t value_type,
size_t value_size);

#ifdef FLECS_META
FLECS_API
Expand Down Expand Up @@ -17708,7 +17720,9 @@ struct string_view : string {
#include <string.h>
#include <limits>

#define FLECS_ENUM_MAX(T) _::to_constant<T, 128>::value
// 126, so that FLECS_ENUM_MAX_COUNT is 127 which is the largest value
// representable by an int8_t.
#define FLECS_ENUM_MAX(T) _::to_constant<T, 126>::value
#define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1)

#ifndef FLECS_CPP_ENUM_REFLECTION_SUPPORT
Expand Down Expand Up @@ -18018,7 +18032,7 @@ struct enum_type {
// Constant is valid, so fill reflection data.
auto v = Value;
const char *name = enum_constant_to_name<E, flecs_enum_cast(E, Value)>();

++enum_type<E>::data.max; // Increment cursor as we build constants array.

// If the enum was previously contiguous, and continues to be through the current value...
Expand All @@ -18040,7 +18054,7 @@ struct enum_type {
}

flecs::entity_t constant = ecs_cpp_enum_constant_register(
world, type<E>::id(world), 0, name, static_cast<int32_t>(v));
world, type<E>::id(world), 0, name, &v, type<U>::id(world), sizeof(U));
flecs_component_ids_set(world,
enum_type<E>::data.constants[enum_type<E>::data.max].index,
constant);
Expand Down Expand Up @@ -18076,7 +18090,7 @@ struct enum_type {
data.contiguous_until = 0;

ecs_log_push();
ecs_cpp_enum_init(world, id);
ecs_cpp_enum_init(world, id, type<U>::id(world));
// data.id = id;

// Generate reflection data
Expand Down Expand Up @@ -27923,8 +27937,21 @@ flecs::opaque<T, ElemType> opaque(flecs::id_t as_type) {

/** Add constant. */
component<T>& constant(const char *name, T value) {
int32_t v = static_cast<int32_t>(value);
untyped_component::constant(name, v);
using U = typename std::underlying_type<T>::type;

ecs_add_id(world_, id_, _::type<flecs::Enum>::id(world_));

ecs_entity_desc_t desc = {};
desc.name = name;
desc.parent = id_;
ecs_entity_t eid = ecs_entity_init(world_, &desc);
ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL);

flecs::id_t pair = ecs_pair(flecs::Constant, _::type<U>::id(world_));
U *ptr = static_cast<U*>(ecs_ensure_id(world_, eid, pair));
*ptr = static_cast<U>(value);
ecs_modified_id(world_, eid, pair);

return *this;
}

Expand Down Expand Up @@ -28993,11 +29020,16 @@ const T* entity_view::get() const {
entity_t c = ecs_get_target(world_, id_, r, 0);

if (c) {
// Get constant value from constant entity
const T* v = static_cast<const T*>(ecs_get_id(world_, c, r));
ecs_assert(v != NULL, ECS_INTERNAL_ERROR,
"missing enum constant value");
#ifdef FLECS_META
using U = typename std::underlying_type<T>::type;
const T* v = static_cast<const T*>(
ecs_get_id(world_, c, ecs_pair(flecs::Constant, _::type<U>::id(world_))));
ecs_assert(v != NULL, ECS_INTERNAL_ERROR, "missing enum constant value");
return v;
#else
// Fallback if we don't have the reflection addon
return static_cast<const T*>(ecs_get_id(world_, id_, r));
#endif
} else {
// If there is no matching pair for (r, *), try just r
return static_cast<const T*>(ecs_get_id(world_, id_, r));
Expand Down Expand Up @@ -31048,9 +31080,17 @@ namespace flecs {

template <typename E>
inline E entity_view::to_constant() const {
const E* ptr = this->get<E>();
#ifdef FLECS_META
using U = typename std::underlying_type<E>::type;
const E* ptr = static_cast<const E*>(ecs_get_id(world_, id_,
ecs_pair(flecs::Constant, _::type<U>::id(world_))));
ecs_assert(ptr != NULL, ECS_INVALID_PARAMETER, "entity is not a constant");
return ptr[0];
#else
ecs_assert(false, ECS_UNSUPPORTED,
"operation not supported without FLECS_META addon");
return E();
#endif
}

template <typename E, if_t< is_enum<E>::value >>
Expand Down
13 changes: 9 additions & 4 deletions include/flecs/addons/cpp/mixins/entity/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,16 @@ const T* entity_view::get() const {
entity_t c = ecs_get_target(world_, id_, r, 0);

if (c) {
// Get constant value from constant entity
const T* v = static_cast<const T*>(ecs_get_id(world_, c, r));
ecs_assert(v != NULL, ECS_INTERNAL_ERROR,
"missing enum constant value");
#ifdef FLECS_META
using U = typename std::underlying_type<T>::type;
const T* v = static_cast<const T*>(
ecs_get_id(world_, c, ecs_pair(flecs::Constant, _::type<U>::id(world_))));
ecs_assert(v != NULL, ECS_INTERNAL_ERROR, "missing enum constant value");
return v;
#else
// Fallback if we don't have the reflection addon
return static_cast<const T*>(ecs_get_id(world_, id_, r));
#endif
} else {
// If there is no matching pair for (r, *), try just r
return static_cast<const T*>(ecs_get_id(world_, id_, r));
Expand Down
10 changes: 9 additions & 1 deletion include/flecs/addons/cpp/mixins/enum/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ namespace flecs {

template <typename E>
inline E entity_view::to_constant() const {
const E* ptr = this->get<E>();
#ifdef FLECS_META
using U = typename std::underlying_type<E>::type;
const E* ptr = static_cast<const E*>(ecs_get_id(world_, id_,
ecs_pair(flecs::Constant, _::type<U>::id(world_))));
ecs_assert(ptr != NULL, ECS_INVALID_PARAMETER, "entity is not a constant");
return ptr[0];
#else
ecs_assert(false, ECS_UNSUPPORTED,
"operation not supported without FLECS_META addon");
return E();
#endif
}

template <typename E, if_t< is_enum<E>::value >>
Expand Down
17 changes: 15 additions & 2 deletions include/flecs/addons/cpp/mixins/meta/component.inl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,20 @@ flecs::opaque<T, ElemType> opaque(flecs::id_t as_type) {

/** Add constant. */
component<T>& constant(const char *name, T value) {
int32_t v = static_cast<int32_t>(value);
untyped_component::constant(name, v);
using U = typename std::underlying_type<T>::type;

ecs_add_id(world_, id_, _::type<flecs::Enum>::id(world_));

ecs_entity_desc_t desc = {};
desc.name = name;
desc.parent = id_;
ecs_entity_t eid = ecs_entity_init(world_, &desc);
ecs_assert(eid != 0, ECS_INTERNAL_ERROR, NULL);

flecs::id_t pair = ecs_pair(flecs::Constant, _::type<U>::id(world_));
U *ptr = static_cast<U*>(ecs_ensure_id(world_, eid, pair));
*ptr = static_cast<U>(value);
ecs_modified_id(world_, eid, pair);

return *this;
}
10 changes: 6 additions & 4 deletions include/flecs/addons/cpp/utils/enum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include <string.h>
#include <limits>

#define FLECS_ENUM_MAX(T) _::to_constant<T, 128>::value
// 126, so that FLECS_ENUM_MAX_COUNT is 127 which is the largest value
// representable by an int8_t.
#define FLECS_ENUM_MAX(T) _::to_constant<T, 126>::value
#define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1)

#ifndef FLECS_CPP_ENUM_REFLECTION_SUPPORT
Expand Down Expand Up @@ -319,7 +321,7 @@ struct enum_type {
// Constant is valid, so fill reflection data.
auto v = Value;
const char *name = enum_constant_to_name<E, flecs_enum_cast(E, Value)>();

++enum_type<E>::data.max; // Increment cursor as we build constants array.

// If the enum was previously contiguous, and continues to be through the current value...
Expand All @@ -341,7 +343,7 @@ struct enum_type {
}

flecs::entity_t constant = ecs_cpp_enum_constant_register(
world, type<E>::id(world), 0, name, static_cast<int32_t>(v));
world, type<E>::id(world), 0, name, &v, type<U>::id(world), sizeof(U));
flecs_component_ids_set(world,
enum_type<E>::data.constants[enum_type<E>::data.max].index,
constant);
Expand Down Expand Up @@ -377,7 +379,7 @@ struct enum_type {
data.contiguous_until = 0;

ecs_log_push();
ecs_cpp_enum_init(world, id);
ecs_cpp_enum_init(world, id, type<U>::id(world));
// data.id = id;

// Generate reflection data
Expand Down
7 changes: 5 additions & 2 deletions include/flecs/addons/flecs_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,18 @@ ecs_entity_t ecs_cpp_component_register(
FLECS_API
void ecs_cpp_enum_init(
ecs_world_t *world,
ecs_entity_t id);
ecs_entity_t id,
ecs_entity_t underlying_type);

FLECS_API
ecs_entity_t ecs_cpp_enum_constant_register(
ecs_world_t *world,
ecs_entity_t parent,
ecs_entity_t id,
const char *name,
int value);
void *value,
ecs_entity_t value_type,
size_t value_size);

#ifdef FLECS_META
FLECS_API
Expand Down
13 changes: 11 additions & 2 deletions include/flecs/addons/meta.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,19 @@ typedef struct ecs_enum_constant_t {
const char *name;

/** May be set when used with ecs_enum_desc_t */
int32_t value;
int64_t value;

/** For when the underlying type is unsigned */
uint64_t value_unsigned;

/** Should not be set by ecs_enum_desc_t */
ecs_entity_t constant;
} ecs_enum_constant_t;

/** Component added to enum type entities */
typedef struct EcsEnum {
ecs_entity_t underlying_type;

/** Populated from child entities with Constant component */
ecs_map_t constants; /**< map<i32_t, ecs_enum_constant_t> */
} EcsEnum;
Expand All @@ -291,7 +296,10 @@ typedef struct ecs_bitmask_constant_t {
const char *name;

/** May be set when used with ecs_bitmask_desc_t */
ecs_flags32_t value;
ecs_flags64_t value;

/** Keep layout the same with ecs_enum_constant_t */
int64_t _unused;

/** Should not be set by ecs_bitmask_desc_t */
ecs_entity_t constant;
Expand Down Expand Up @@ -960,6 +968,7 @@ ecs_entity_t ecs_primitive_init(
typedef struct ecs_enum_desc_t {
ecs_entity_t entity; /**< Existing entity to use for type (optional). */
ecs_enum_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; /**< Enum constants. */
ecs_entity_t underlying_type;
} ecs_enum_desc_t;

/** Create a new enum type.
Expand Down
Loading

0 comments on commit 0bf9948

Please sign in to comment.