Flecs: include/flecs/addons/cpp/utils/enum.hpp Source File
Description: A fast entity component system (ECS) for C & C++

| Flecs v4.1 A fast entity component system (ECS) for C & C++

Loading...
Searching...
No Matches
enum.hpp
Go to the documentation of this file.
1
8
9#include <limits>
10
11// 126, so that FLECS_ENUM_MAX_COUNT is 127, which is the largest value
12// representable by an int8_t.
13#define FLECS_ENUM_MAX(T) _::to_constant<T, 126>::value
14#define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1)
15
16// Flag to turn off enum reflection.
17#ifdef FLECS_CPP_NO_ENUM_REFLECTION
18#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 0
19#endif
20
21// Test if we're using a compiler that supports the required features.
22#ifndef FLECS_CPP_ENUM_REFLECTION_SUPPORT
23#if !defined(__clang__) && defined(__GNUC__)
24#if __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 5)
25#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
26#else
27#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 0
28#endif
29#else
30#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
31#endif
32#endif
33
34#if defined(__clang__) && __clang_major__ >= 16
35// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307
36#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v)
37#elif defined(__GNUC__) && __GNUC__ > 10
38#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v)
39#else
40#define flecs_enum_cast(T, v) static_cast<T>(v)
41#endif
42
43namespace flecs {
44
46namespace _ {
48template <typename E, underlying_type_t<E> Value>
49struct to_constant {
50 static constexpr E value = flecs_enum_cast(E, Value);
51};
52
53template <typename E, underlying_type_t<E> Value>
54constexpr E to_constant<E, Value>::value;
55}
56
58template <typename E>
59struct enum_data;
60
66template <typename E>
67static enum_data<E> enum_type(flecs::world_t *world);
68
72template <typename E>
73 struct enum_last {
74 static constexpr E value = FLECS_ENUM_MAX(E);
75};
76
77/* Utility macro to override the enum_last trait. */
78#define FLECS_ENUM_LAST(T, Last)\
79 namespace flecs {\
80 template<>\
81 struct enum_last<T> {\
82 static constexpr T value = Last;\
83 };\
84 }
85
86namespace _ {
87
88#if INTPTR_MAX == INT64_MAX
89 #ifdef ECS_TARGET_MSVC
90 #if _MSC_VER >= 1929
91 #define ECS_SIZE_T_STR "unsigned __int64"
92 #else
93 #define ECS_SIZE_T_STR "unsigned int"
94 #endif
95 #elif defined(__clang__)
96 #define ECS_SIZE_T_STR "size_t"
97 #else
98 #ifdef ECS_TARGET_WINDOWS
99 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long long unsigned int"
100 #else
101 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long unsigned int"
102 #endif
103 #endif
104#else
105 #ifdef ECS_TARGET_MSVC
106 #if _MSC_VER >= 1929
107 #define ECS_SIZE_T_STR "unsigned __int32"
108 #else
109 #define ECS_SIZE_T_STR "unsigned int"
110 #endif
111 #elif defined(__clang__)
112 #define ECS_SIZE_T_STR "size_t"
113 #else
114 #ifdef ECS_TARGET_WINDOWS
115 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
116 #else
117 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
118 #endif
119 #endif
120#endif
121
123template <typename E>
124constexpr size_t enum_type_len() {
125 return ECS_FUNC_TYPE_LEN(, enum_type_len, ECS_FUNC_NAME)
126 - (sizeof(ECS_SIZE_T_STR) - 1u);
127}
128
134#if defined(ECS_TARGET_CLANG)
135#if ECS_CLANG_VERSION < 13
136template <typename E, E C>
137constexpr bool enum_constant_is_valid() {
138 return !((
139 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
140 enum_type_len<E>() + 6 /* ', C = ' */] >= '0') &&
141 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
142 enum_type_len<E>() + 6 /* ', C = ' */] <= '9')) ||
143 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
144 enum_type_len<E>() + 6 /* ', C = ' */] == '-'));
145}
146#else
147template <typename E, E C>
148constexpr bool enum_constant_is_valid() {
149 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
150 enum_type_len<E>() + 6 /* ', C = ' */] != '(');
151}
152#endif
153#elif defined(ECS_TARGET_GNU)
154template <typename E, E C>
155constexpr bool enum_constant_is_valid() {
156 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constexpr bool, enum_constant_is_valid) +
157 enum_type_len<E>() + 8 /* ', E C = ' */] != '(');
158}
159#else
160template <size_t N>
161 constexpr size_t enum_template_arg_separator(
162 const char (&func_name)[N],
163 size_t pos,
164 size_t end,
165 size_t depth = 0)
166{
167 return pos >= end
168 ? end
169 : func_name[pos] == '<'
170 ? enum_template_arg_separator(func_name, pos + 1, end, depth + 1)
171 : func_name[pos] == '>'
172 ? enum_template_arg_separator(
173 func_name, pos + 1, end, depth ? depth - 1 : 0)
174 : (func_name[pos] == ',' && !depth)
175 ? pos
176 : enum_template_arg_separator(
177 func_name, pos + 1, end, depth);
178}
179
180/* Use a different trick on MSVC, since it uses a hexadecimal representation for
181 * invalid enum constants. We can leverage that MSVC inserts a C-style cast
182 * into the name. Find the template argument separator structurally instead of
183 * relying on the exact spelling of __FUNCSIG__ for the enum type. */
184template <typename E, E C>
185constexpr bool enum_constant_is_valid() {
186 return enum_template_arg_separator(
187 ECS_FUNC_NAME,
188 ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid),
189 sizeof(ECS_FUNC_NAME) - ECS_FUNC_NAME_BACK - 1u) <
190 (sizeof(ECS_FUNC_NAME) - ECS_FUNC_NAME_BACK - 1u) &&
191 ECS_FUNC_NAME[enum_template_arg_separator(
192 ECS_FUNC_NAME,
193 ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid),
194 sizeof(ECS_FUNC_NAME) - ECS_FUNC_NAME_BACK - 1u) + 1u] != '(';
195}
196#endif
197
199template <typename E, underlying_type_t<E> C>
200constexpr bool enum_constant_is_valid_wrap() {
201 return enum_constant_is_valid<E, flecs_enum_cast(E, C)>();
202}
203
205template <typename E, E C>
206struct enum_is_valid {
207 static constexpr bool value = enum_constant_is_valid<E, C>();
208};
209
211template <typename E, E C>
212static const char* enum_constant_to_name() {
213 static const size_t len = ECS_FUNC_TYPE_LEN(
214 const char*, enum_constant_to_name, ECS_FUNC_NAME);
215 static char result[len + 1] = {};
216 return ecs_cpp_get_constant_name(
217 result, ECS_FUNC_NAME, string::length(ECS_FUNC_NAME),
218 ECS_FUNC_NAME_BACK);
219}
220
231template <typename E, typename Handler>
232 struct enum_reflection {
233 using U = underlying_type_t<E>;
234
251 template <U Low, U High, typename... Args>
252 static constexpr U each_enum_range(U last_value, Args&... args) {
253 return High - Low <= 1
254 ? High == Low
255 ? Handler::template handle_constant<Low>(last_value, args...)
256 : Handler::template handle_constant<High>(
257 Handler::template handle_constant<Low>(last_value, args...),
258 args...)
259 : each_enum_range<(Low + High) / 2 + 1, High>(
260 each_enum_range<Low, (Low + High) / 2>(last_value, args...),
261 args...
262 );
263 }
264
281 template <U Low, U High, typename... Args>
282 static constexpr U each_mask_range(U last_value, Args&... args) {
283 // If Low shares any bits with Current Flag, or if High is less
284 // than/equal to Low (and High isn't negative because max-flag signed)
285 return (Low & High) || (High <= Low && High != high_bit)
286 ? last_value
287 : Handler::template handle_constant<High>(
288 each_mask_range<Low, ((High >> 1) & ~high_bit)>(last_value, args...),
289 args...
290 );
291 }
292
305 template <U Value = static_cast<U>(FLECS_ENUM_MAX(E)), typename... Args>
306 static constexpr U each_enum(Args&... args) {
307 return each_mask_range<Value, high_bit>(
308 each_enum_range<0, Value>(0, args...), args...);
309 }
310 /* To avoid warnings with bit manipulation, calculate the high bit with an
311 unsigned type of the same size. */
312 using UU = typename std::make_unsigned<U>::type;
313 static const U high_bit =
314 static_cast<U>(static_cast<UU>(1) << (sizeof(UU) * 8 - 1));
315};
316
320template<typename T>
321 struct enum_constant {
323 int32_t index;
325 T value;
327 T offset;
329 const char *name;
330};
331
333template <typename E>
334struct enum_type {
335private:
336 using This = enum_type<E>;
337 using U = underlying_type_t<E>;
338
342 struct reflection_count {
343 template <U Value>
344 static constexpr U handle_constant(U last_value) {
345 if constexpr (enum_constant_is_valid_wrap<E, Value>()) {
346 return 1 + last_value;
347 } else {
348 return last_value;
349 }
350 }
351 };
352
360 struct reflection_init {
361 template <U Value>
362 static U handle_constant(U last_value, This& me) {
363 if constexpr (enum_constant_is_valid_wrap<E, Value>()) {
364 // Constant is valid, so fill the reflection data.
365 auto v = Value;
366 const char *name = enum_constant_to_name<E, flecs_enum_cast(E, Value)>();
367
368 ++me.max; // Increment cursor as we build the constants array.
369
370 // If the enum was previously contiguous, and continues to be
371 // through the current value...
372 if (me.has_contiguous && static_cast<U>(me.max) == v && me.contiguous_until == v) {
373 ++me.contiguous_until;
374 }
375
376 // else, if the enum was never contiguous and hasn't been set as not
377 // contiguous...
378 else if (!me.contiguous_until && me.has_contiguous) {
379 me.has_contiguous = false;
380 }
381
382 ecs_assert(!(last_value > 0 &&
383 v < std::numeric_limits<U>::min() + last_value),
384 ECS_UNSUPPORTED,
385 "Signed integer enums cause integer overflow when recording "
386 "offset from high positive to low negative. Consider using "
387 "unsigned integers as the underlying type.");
388
389 me.constants[me.max].value = v;
390 me.constants[me.max].offset = v - last_value;
391 me.constants[me.max].name = name;
392 if (!me.constants[me.max].index) {
393 me.constants[me.max].index =
394 flecs_component_ids_index_get();
395 }
396
397 return v;
398 } else {
399 // Search for the constant failed. Pass the last valid value through.
400 return last_value;
401 }
402 }
403 };
404public:
405
407 enum_type() {
408 // Initialize/reset reflection data values to the default state.
409 min = 0;
410 max = -1;
411 has_contiguous = true;
412 contiguous_until = 0;
413
414#if FLECS_CPP_ENUM_REFLECTION_SUPPORT
415 enum_reflection<E, reflection_init>::
416 template each_enum< static_cast<U>(enum_last<E>::value) >(*this);
417#endif
418 }
419
421 static enum_type<E>& get() {
422 static _::enum_type<E> instance;
423 return instance;
424 }
425
427 flecs::entity_t entity(E value) const {
428 int index = index_by_value(value);
429 if (index >= 0) {
430 return constants[index].id;
431 }
432 return 0;
433 }
434
436 void register_for_world(flecs::world_t *world, flecs::entity_t id) {
437#if !FLECS_CPP_ENUM_REFLECTION_SUPPORT
438 ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher")
439#endif
440
441 ecs_log_push();
442 ecs_cpp_enum_init(world, id, type<U>::id(world));
443
444 for (U v = 0; v < static_cast<U>(max + 1); v ++) {
445 if (constants[v].index) {
446 flecs::entity_t constant = ecs_cpp_enum_constant_register(world,
447 type<E>::id(world), 0, constants[v].name, &constants[v].value,
448 type<U>::id(world), sizeof(U));
449
450 flecs_component_ids_set(world, constants[v].index, constant);
451 }
452 }
453
454 ecs_log_pop();
455 }
456
457 int min;
458 int max;
459
460 // If enum constants start non-sparse, contiguous_until will be the index of
461 // the first sparse value, or the end of the constants array.
462 U contiguous_until;
463
464 // Compile-time-generated count of enum constants.
465 static constexpr unsigned int constants_size =
466 enum_reflection<E, reflection_count>::
467 template each_enum< static_cast<U>(enum_last<E>::value) >();
468
469 // Constants array is sized to the number of found constants, or 1
470 // to avoid a zero-sized array.
471 #ifdef FLECS_CPP_ENUM_REFLECTION
472 enum_constant<U> constants[constants_size? constants_size: 1] = {};
473 bool has_contiguous;
474 #else
475 // If we're not using enum reflection, we cannot statically determine the
476 // upper bound of the enum, so use 128.
477 enum_constant<U> constants[128] = {};
478 bool has_contiguous = true; // Assume contiguous IDs.
479 #endif
480};
481
483template <typename E>
484inline static void init_enum(flecs::world_t *world, flecs::entity_t id) {
485 (void)world; (void)id;
486 if constexpr (is_enum_v<E>) {
487 _::enum_type<E>::get().register_for_world(world, id);
488 }
489}
490
491} // namespace _
492
494template <typename E>
495 struct enum_data {
496 using U = underlying_type_t<E>;
497
499 enum_data(flecs::world_t *world, _::enum_type<E>& impl)
500 : world_(world)
501 , impl_(impl) { }
502
510 bool is_valid(U value) {
511 int index = index_by_value(value);
512 if (index < 0) {
513 return false;
514 }
515 return impl_.constants[index].index != 0;
516 }
517
525 bool is_valid(E value) {
526 return is_valid(static_cast<U>(value));
527 }
528
535 int index_by_value(U value) const {
536 if (impl_.max < 0) {
537 return -1;
538 }
539
540 // Check if value is in the contiguous lookup section.
541 if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) {
542 return static_cast<int>(value);
543 }
544 U accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0;
545 for (int i = static_cast<int>(impl_.contiguous_until); i <= impl_.max; ++i) {
546 accumulator += impl_.constants[i].offset;
547 if (accumulator == value) {
548 return i;
549 }
550 }
551 return -1;
552 }
553
560 int index_by_value(E value) const {
561 return index_by_value(static_cast<U>(value));
562 }
563
565 int first() const {
566 return impl_.min;
567 }
568
570 int last() const {
571 return impl_.max;
572 }
573
575 int next(int cur) const {
576 return cur + 1;
577 }
578
580 flecs::entity entity() const;
582 flecs::entity entity(U value) const;
584 flecs::entity entity(E value) const;
585
592 #ifdef FLECS_CPP_NO_ENUM_REFLECTION
593 void register_constant(flecs::world_t *world, U v, flecs::entity_t e) {
594 if (v < 128) {
595 if (!impl_.constants[v].index) {
596 impl_.constants[v].index = flecs_component_ids_index_get();
597 }
598
599 flecs_component_ids_set(world, impl_.constants[v].index, e);
600
601 impl_.max ++;
602
603 if (impl_.contiguous_until <= v) {
604 impl_.contiguous_until = v + 1;
605 }
606 }
607 }
608 #endif
609
610 flecs::world_t *world_;
611 _::enum_type<E>& impl_;
612};
613
615template <typename E>
616 enum_data<E> enum_type(flecs::world_t *world) {
617 _::type<E>::id(world); // Ensure the enum is registered.
618 auto& ref = _::enum_type<E>::get();
619 return enum_data<E>(world, ref);
620}
621
622} // namespace flecs
ecs_assert
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
ECS_UNSUPPORTED
#define ECS_UNSUPPORTED
Unsupported error code.
Definition log.h:679
ecs_log_push
#define ecs_log_push()
Push log indentation at the default level.
Definition log.h:458
ecs_abort
#define ecs_abort(error_code,...)
Abort.
Definition log.h:464
ecs_log_pop
#define ecs_log_pop()
Pop log indentation at the default level.
Definition log.h:460
flecs::component::constant
untyped_component & constant(const char *name, T value)
Add a constant.
Definition untyped_component.inl:186
flecs::entity_t
ecs_entity_t entity_t
Entity type.
Definition c_types.hpp:21
flecs::world_t
ecs_world_t world_t
World type.
Definition c_types.hpp:18
flecs::_
Int to enum.
Definition component.hpp:18
flecs::_::enum_template_arg_separator
constexpr size_t enum_template_arg_separator(const char(&func_name)[N], size_t pos, size_t end, size_t depth=0)
Test if a value is valid for an enumeration.
Definition enum.hpp:161
flecs::_::enum_constant
Enumeration constant data.
Definition enum.hpp:321
flecs::_::enum_constant::offset
T offset
Offset from the previous constant value.
Definition enum.hpp:327
flecs::_::enum_constant::name
const char * name
The constant name.
Definition enum.hpp:329
flecs::_::enum_constant::index
int32_t index
Global index used to obtain a world-local entity ID.
Definition enum.hpp:323
flecs::_::enum_constant::value
T value
The constant value.
Definition enum.hpp:325
flecs::_::enum_reflection
Provide utilities for enum reflection.
Definition enum.hpp:232
flecs::_::enum_reflection::each_enum_range
static constexpr U each_enum_range(U last_value, Args &... args)
Iterate over the range [Low, High] of enum values between Low and High.
Definition enum.hpp:252
flecs::_::enum_reflection::each_enum
static constexpr U each_enum(Args &... args)
Handle enum iteration for gathering reflection data.
Definition enum.hpp:306
flecs::_::enum_reflection::each_mask_range
static constexpr U each_mask_range(U last_value, Args &... args)
Iterate over the mask range (Low, High] of enum values between Low and High.
Definition enum.hpp:282
flecs::_::type
Definition flecs.hpp:39
flecs::entity
Entity.
Definition entity.hpp:30
flecs::enum_data
Convenience type with enum reflection data.
Definition enum.hpp:495
flecs::enum_data::last
int last() const
Return the index of the last constant.
Definition enum.hpp:570
flecs::enum_data::entity
flecs::entity entity() const
Get entity for the enum type.
Definition world.hpp:440
flecs::enum_data::next
int next(int cur) const
Return the next constant index after the given one.
Definition enum.hpp:575
flecs::enum_data::index_by_value
int index_by_value(E value) const
Find the index into the constants array for an enum value, if one exists.
Definition enum.hpp:560
flecs::enum_data::is_valid
bool is_valid(U value)
Check if a given integral value is a valid enum value.
Definition enum.hpp:510
flecs::enum_data::enum_data
enum_data(flecs::world_t *world, _::enum_type< E > &impl)
Construct enum_data from a world and an enum_type implementation.
Definition enum.hpp:499
flecs::enum_data::world_
flecs::world_t * world_
Manually register a constant for an enum.
Definition enum.hpp:610
flecs::enum_data::index_by_value
int index_by_value(U value) const
Find the index into the constants array for a value, if one exists.
Definition enum.hpp:535
flecs::enum_data::is_valid
bool is_valid(E value)
Check if a given enum value is valid.
Definition enum.hpp:525
flecs::enum_data::first
int first() const
Return the index of the first constant.
Definition enum.hpp:565
flecs::enum_last
Trait to define the last valid enum value for reflection.
Definition enum.hpp:73
flecs::ref
Component reference.
Definition ref.hpp:109
flecs::string::length
std::size_t length() const
Return the string length.
Definition string.hpp:114
flecs::world
The world.
Definition world.hpp:246