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
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>
73struct 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>
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] == '>'
173 func_name, pos + 1, end, depth ? depth - 1 : 0)
174 : (func_name[pos] == ',' && !depth)
175 ? pos
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() {
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] = {};
217 result, ECS_FUNC_NAME, string::length(ECS_FUNC_NAME),
218 ECS_FUNC_NAME_BACK);
219}
220
231template <typename E, typename Handler>
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>
322#ifdef FLECS_MULTI_WORLD
324 int32_t index;
325#else
328#endif
334 const char *name;
335
336 bool discovered() const {
337#ifdef FLECS_MULTI_WORLD
338 return index != 0;
339#else
340 return name != nullptr || id != 0;
341#endif
342 }
343};
344
346template <typename E>
347struct enum_type {
348private:
349 using This = enum_type<E>;
350 using U = underlying_type_t<E>;
351
355 struct reflection_count {
356 template <U Value>
357 static constexpr U handle_constant(U last_value) {
358 if constexpr (enum_constant_is_valid_wrap<E, Value>()) {
359 return 1 + last_value;
360 } else {
361 return last_value;
362 }
363 }
364 };
365
373 struct reflection_init {
374 template <U Value>
375 static U handle_constant(U last_value, This& me) {
376 if constexpr (enum_constant_is_valid_wrap<E, Value>()) {
377 // Constant is valid, so fill the reflection data.
378 auto v = Value;
379 const char *name = enum_constant_to_name<E, flecs_enum_cast(E, Value)>();
380
381 ++me.max; // Increment cursor as we build the constants array.
382
383 // If the enum was previously contiguous, and continues to be
384 // through the current value...
385 if (me.has_contiguous && static_cast<U>(me.max) == v && me.contiguous_until == v) {
386 ++me.contiguous_until;
387 }
388
389 // else, if the enum was never contiguous and hasn't been set as not
390 // contiguous...
391 else if (!me.contiguous_until && me.has_contiguous) {
392 me.has_contiguous = false;
393 }
394
395 ecs_assert(!(last_value > 0 &&
396 v < std::numeric_limits<U>::min() + last_value),
398 "Signed integer enums cause integer overflow when recording "
399 "offset from high positive to low negative. Consider using "
400 "unsigned integers as the underlying type.");
401
402 me.constants[me.max].value = v;
403 me.constants[me.max].offset = v - last_value;
404 me.constants[me.max].name = name;
405#ifdef FLECS_MULTI_WORLD
406 if (!me.constants[me.max].index) {
407 me.constants[me.max].index =
408 flecs_component_ids_index_get();
409 }
410#endif
411
412 return v;
413 } else {
414 // Search for the constant failed. Pass the last valid value through.
415 return last_value;
416 }
417 }
418 };
419public:
420
422 enum_type() {
423 // Initialize/reset reflection data values to the default state.
424 min = 0;
425 max = -1;
426 has_contiguous = true;
427 contiguous_until = 0;
428
429#if FLECS_CPP_ENUM_REFLECTION_SUPPORT
430 enum_reflection<E, reflection_init>::
431 template each_enum< static_cast<U>(enum_last<E>::value) >(*this);
432#endif
433 }
434
436 static enum_type<E>& get() {
437 static _::enum_type<E> instance;
438 return instance;
439 }
440
441#ifndef FLECS_MULTI_WORLD
443 flecs::entity_t entity(E value) const {
444 int index = index_by_value(value);
445 if (index >= 0) {
446 return constants[index].id;
447 }
448 return 0;
449 }
450#endif
451
453 void register_for_world(flecs::world_t *world, flecs::entity_t id) {
454#if !FLECS_CPP_ENUM_REFLECTION_SUPPORT
455 ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher")
456#endif
457
458 ecs_log_push();
459 ecs_cpp_enum_init(world, id, type<U>::id(world));
460
461 for (U v = 0; v < static_cast<U>(max + 1); v ++) {
462 if (constants[v].discovered()) {
464 type<E>::id(world), 0, constants[v].name, &constants[v].value,
465 type<U>::id(world), sizeof(U));
466
467#ifdef FLECS_MULTI_WORLD
468 flecs_component_ids_set(world, constants[v].index, constant);
469#else
470 constants[v].id = constant;
471#endif
472 }
473 }
474
475 ecs_log_pop();
476 }
477
478 int min;
479 int max;
480
481 // If enum constants start non-sparse, contiguous_until will be the index of
482 // the first sparse value, or the end of the constants array.
483 U contiguous_until;
484
485 // Compile-time-generated count of enum constants.
486 static constexpr unsigned int constants_size =
487 enum_reflection<E, reflection_count>::
488 template each_enum< static_cast<U>(enum_last<E>::value) >();
489
490 // Constants array is sized to the number of found constants, or 1
491 // to avoid a zero-sized array.
492 #ifdef FLECS_CPP_ENUM_REFLECTION
493 enum_constant<U> constants[constants_size? constants_size: 1] = {};
494 bool has_contiguous;
495 #else
496 // If we're not using enum reflection, we cannot statically determine the
497 // upper bound of the enum, so use 128.
498 enum_constant<U> constants[128] = {};
499 bool has_contiguous = true; // Assume contiguous IDs.
500 #endif
501};
502
504template <typename E>
505inline static void init_enum(flecs::world_t *world, flecs::entity_t id) {
506 (void)world; (void)id;
507 if constexpr (is_enum_v<E>) {
508 _::enum_type<E>::get().register_for_world(world, id);
509 }
510}
511
512} // namespace _
513
515template <typename E>
516struct enum_data {
517 using U = underlying_type_t<E>;
518
520 enum_data(flecs::world_t *world, _::enum_type<E>& impl)
521 : world_(world)
522 , impl_(impl) { }
523
531 bool is_valid(U value) {
532 int index = index_by_value(value);
533 if (index < 0) {
534 return false;
535 }
536 return impl_.constants[index].discovered();
537 }
538
546 bool is_valid(E value) {
547 return is_valid(static_cast<U>(value));
548 }
549
556 int index_by_value(U value) const {
557 if (impl_.max < 0) {
558 return -1;
559 }
560
561 // Check if value is in the contiguous lookup section.
562 if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) {
563 return static_cast<int>(value);
564 }
565 U accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0;
566 for (int i = static_cast<int>(impl_.contiguous_until); i <= impl_.max; ++i) {
567 accumulator += impl_.constants[i].offset;
568 if (accumulator == value) {
569 return i;
570 }
571 }
572 return -1;
573 }
574
581 int index_by_value(E value) const {
582 return index_by_value(static_cast<U>(value));
583 }
584
586 int first() const {
587 return impl_.min;
588 }
589
591 int last() const {
592 return impl_.max;
593 }
594
596 int next(int cur) const {
597 return cur + 1;
598 }
599
601 flecs::entity entity() const;
603 flecs::entity entity(U value) const;
605 flecs::entity entity(E value) const;
606
613 #ifdef FLECS_CPP_NO_ENUM_REFLECTION
614 void register_constant(flecs::world_t *world, U v, flecs::entity_t e) {
615 if (v < 128) {
616#ifdef FLECS_MULTI_WORLD
617 if (!impl_.constants[v].index) {
618 impl_.constants[v].index = flecs_component_ids_index_get();
619 }
620#endif
621
622#ifdef FLECS_MULTI_WORLD
623 flecs_component_ids_set(world, impl_.constants[v].index, e);
624#else
625 (void)world;
626 impl_.constants[v].id = e;
627#endif
628
629 impl_.max ++;
630
631 if (impl_.contiguous_until <= v) {
632 impl_.contiguous_until = v + 1;
633 }
634 }
635 }
636 #endif
637
639 _::enum_type<E>& impl_;
640};
641
643template <typename E>
645 _::type<E>::id(world); // Ensure the enum is registered.
646 auto& ref = _::enum_type<E>::get();
647 return enum_data<E>(world, ref);
648}
649
650} // namespace flecs
component< T > & constant(const char *name, T value)
Add a constant.
Definition component.inl:38
FLECS_API void ecs_cpp_enum_init(ecs_world_t *world, ecs_entity_t id, ecs_entity_t underlying_type)
Initialize a C++ enum 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, void *value, ecs_entity_t value_type, size_t value_size)
Register a C++ enum constant.
FLECS_API char * ecs_cpp_get_constant_name(char *constant_name, const char *func_name, size_t len, size_t back_len)
Get constant name from compiler-generated function name.
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
#define ECS_UNSUPPORTED
Unsupported error code.
Definition log.h:679
#define ecs_log_push()
Push log indentation at the default level.
Definition log.h:458
#define ecs_abort(error_code,...)
Abort.
Definition log.h:464
#define ecs_log_pop()
Pop log indentation at the default level.
Definition log.h:460
ecs_entity_t entity_t
Entity type.
Definition c_types.hpp:21
ecs_world_t world_t
World type.
Definition c_types.hpp:18
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
Enumeration constant data.
Definition enum.hpp:321
T offset
Offset from the previous constant value.
Definition enum.hpp:332
const char * name
The constant name.
Definition enum.hpp:334
int32_t index
Global index used to obtain a world-local entity ID.
Definition enum.hpp:324
T value
The constant value.
Definition enum.hpp:330
Provide utilities for enum reflection.
Definition enum.hpp:232
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
static constexpr U each_enum(Args &... args)
Handle enum iteration for gathering reflection data.
Definition enum.hpp:306
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
Entity.
Definition entity.hpp:30
Convenience type with enum reflection data.
Definition enum.hpp:516
int last() const
Return the index of the last constant.
Definition enum.hpp:591
flecs::entity entity() const
Get entity for the enum type.
Definition world.hpp:440
int next(int cur) const
Return the next constant index after the given one.
Definition enum.hpp:596
int index_by_value(E value) const
Find the index into the constants array for an enum value, if one exists.
Definition enum.hpp:581
bool is_valid(U value)
Check if a given integral value is a valid enum value.
Definition enum.hpp:531
enum_data(flecs::world_t *world, _::enum_type< E > &impl)
Construct enum_data from a world and an enum_type implementation.
Definition enum.hpp:520
flecs::world_t * world_
Manually register a constant for an enum.
Definition enum.hpp:638
int index_by_value(U value) const
Find the index into the constants array for a value, if one exists.
Definition enum.hpp:556
bool is_valid(E value)
Check if a given enum value is valid.
Definition enum.hpp:546
int first() const
Return the index of the first constant.
Definition enum.hpp:586
Trait to define the last valid enum value for reflection.
Definition enum.hpp:73
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
Component reference.
Definition ref.hpp:109
std::size_t length() const
Return the string length.
Definition string.hpp:114
The world.
Definition world.hpp:244