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

| Flecs v4.0 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 _ {
47template <typename E, underlying_type_t<E> Value>
48 struct to_constant {
49 static constexpr E value = flecs_enum_cast(E, Value);
50};
51
52template <typename E, underlying_type_t<E> Value>
53constexpr E to_constant<E, Value>::value;
54}
55
57template <typename E>
58struct enum_data;
59
60template <typename E>
61static enum_data<E> enum_type(flecs::world_t *world);
62
63template <typename E>
64 struct enum_last {
65 static constexpr E value = FLECS_ENUM_MAX(E);
66};
67
68/* Utility macro to override enum_last trait */
69#define FLECS_ENUM_LAST(T, Last)\
70 namespace flecs {\
71 template<>\
72 struct enum_last<T> {\
73 static constexpr T value = Last;\
74 };\
75 }
76
77namespace _ {
78
79#if INTPTR_MAX == INT64_MAX
80 #ifdef ECS_TARGET_MSVC
81 #if _MSC_VER >= 1929
82 #define ECS_SIZE_T_STR "unsigned __int64"
83 #else
84 #define ECS_SIZE_T_STR "unsigned int"
85 #endif
86 #elif defined(__clang__)
87 #define ECS_SIZE_T_STR "size_t"
88 #else
89 #ifdef ECS_TARGET_WINDOWS
90 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long long unsigned int"
91 #else
92 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long unsigned int"
93 #endif
94 #endif
95#else
96 #ifdef ECS_TARGET_MSVC
97 #if _MSC_VER >= 1929
98 #define ECS_SIZE_T_STR "unsigned __int32"
99 #else
100 #define ECS_SIZE_T_STR "unsigned int"
101 #endif
102 #elif defined(__clang__)
103 #define ECS_SIZE_T_STR "size_t"
104 #else
105 #ifdef ECS_TARGET_WINDOWS
106 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
107 #else
108 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
109 #endif
110 #endif
111#endif
112
113template <typename E>
114constexpr size_t enum_type_len() {
115 return ECS_FUNC_TYPE_LEN(, enum_type_len, ECS_FUNC_NAME)
116 - (sizeof(ECS_SIZE_T_STR) - 1u);
117}
118
123#if defined(ECS_TARGET_CLANG)
124#if ECS_CLANG_VERSION < 13
125template <typename E, E C>
126constexpr bool enum_constant_is_valid() {
127 return !((
128 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
129 enum_type_len<E>() + 6 /* ', C = ' */] >= '0') &&
130 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
131 enum_type_len<E>() + 6 /* ', C = ' */] <= '9')) ||
132 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
133 enum_type_len<E>() + 6 /* ', C = ' */] == '-'));
134}
135#else
136template <typename E, E C>
137constexpr bool enum_constant_is_valid() {
138 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
139 enum_type_len<E>() + 6 /* ', E C = ' */] != '(');
140}
141#endif
142#elif defined(ECS_TARGET_GNU)
143template <typename E, E C>
144constexpr bool enum_constant_is_valid() {
145 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constexpr bool, enum_constant_is_valid) +
146 enum_type_len<E>() + 8 /* ', E C = ' */] != '(');
147}
148#else
149/* Use different trick on MSVC, since it uses hexadecimal representation for
150 * invalid enum constants. We can leverage that msvc inserts a C-style cast
151 * into the name, and the location of its first character ('(') is known. */
152template <typename E, E C>
153 constexpr bool enum_constant_is_valid() {
154 return ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
155 enum_type_len<E>() + 1] != '(';
156}
157#endif
158
159/* Without this wrapper __builtin_bit_cast doesn't work */
160template <typename E, underlying_type_t<E> C>
161constexpr bool enum_constant_is_valid_wrap() {
162 return enum_constant_is_valid<E, flecs_enum_cast(E, C)>();
163}
164
165template <typename E, E C>
166 struct enum_is_valid {
167 static constexpr bool value = enum_constant_is_valid<E, C>();
168};
169
171template <typename E, E C>
172static const char* enum_constant_to_name() {
173 static const size_t len = ECS_FUNC_TYPE_LEN(
174 const char*, enum_constant_to_name, ECS_FUNC_NAME);
175 static char result[len + 1] = {};
176 return ecs_cpp_get_constant_name(
177 result, ECS_FUNC_NAME, string::length(ECS_FUNC_NAME),
178 ECS_FUNC_NAME_BACK);
179}
180
191template <typename E, typename Handler>
192 struct enum_reflection {
193 using U = underlying_type_t<E>;
194
211 template <U Low, U High, typename... Args>
212 static constexpr U each_enum_range(U last_value, Args&... args) {
213 return High - Low <= 1
214 ? High == Low
215 ? Handler::template handle_constant<Low>(last_value, args...)
216 : Handler::template handle_constant<High>(
217 Handler::template handle_constant<Low>(last_value, args...),
218 args...)
219 : each_enum_range<(Low + High) / 2 + 1, High>(
220 each_enum_range<Low, (Low + High) / 2>(last_value, args...),
221 args...
222 );
223 }
224
241 template <U Low, U High, typename... Args>
242 static constexpr U each_mask_range(U last_value, Args&... args) {
243 // If Low shares any bits with Current Flag, or if High is less
244 // than/equal to Low (and High isn't negative because max-flag signed)
245 return (Low & High) || (High <= Low && High != high_bit)
246 ? last_value
247 : Handler::template handle_constant<High>(
248 each_mask_range<Low, ((High >> 1) & ~high_bit)>(last_value, args...),
249 args...
250 );
251 }
252
265 template <U Value = static_cast<U>(FLECS_ENUM_MAX(E)), typename... Args>
266 static constexpr U each_enum(Args&... args) {
267 return each_mask_range<Value, high_bit>(
268 each_enum_range<0, Value>(0, args...), args...);
269 }
270 /* to avoid warnings with bit manipulation, calculate the high bit with an
271 unsigned type of the same size: */
272 using UU = typename std::make_unsigned<U>::type;
273 static const U high_bit =
274 static_cast<U>(static_cast<UU>(1) << (sizeof(UU) * 8 - 1));
275};
276
278template<typename T>
279 struct enum_constant {
280 int32_t index; // Global index used to obtain world local entity id
281 T value;
282 T offset;
283 const char *name;
284};
285
287template <typename E>
288 struct enum_type {
289private:
290 using This = enum_type<E>;
291 using U = underlying_type_t<E>;
292
296 struct reflection_count {
297 template <U Value,
298 flecs::if_not_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
299 static constexpr U handle_constant(U last_value) {
300 return last_value;
301 }
302
303 template <U Value,
304 flecs::if_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
305 static constexpr U handle_constant(U last_value) {
306 return 1 + last_value;
307 }
308 };
309
317 struct reflection_init {
318 template <U Value,
319 flecs::if_not_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
320 static U handle_constant(U last_value, This&) {
321 // Search for constant failed. Pass last valid value through.
322 return last_value;
323 }
324
325 template <U Value,
326 flecs::if_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
327 static U handle_constant(U last_value, This& me) {
328 // Constant is valid, so fill reflection data.
329 auto v = Value;
330 const char *name = enum_constant_to_name<E, flecs_enum_cast(E, Value)>();
331
332 ++me.max; // Increment cursor as we build constants array.
333
334 // If the enum was previously contiguous, and continues to be
335 // through the current value...
336 if (me.has_contiguous && static_cast<U>(me.max) == v && me.contiguous_until == v) {
337 ++me.contiguous_until;
338 }
339
340 // else, if the enum was never contiguous and hasn't been set as not
341 // contiguous...
342 else if (!me.contiguous_until && me.has_contiguous) {
343 me.has_contiguous = false;
344 }
345
346 ecs_assert(!(last_value > 0 &&
347 v < std::numeric_limits<U>::min() + last_value),
348 ECS_UNSUPPORTED,
349 "Signed integer enums causes integer overflow when recording "
350 "offset from high positive to low negative. Consider using "
351 "unsigned integers as underlying type.");
352
353 me.constants[me.max].value = v;
354 me.constants[me.max].offset = v - last_value;
355 me.constants[me.max].name = name;
356 if (!me.constants[me.max].index) {
357 me.constants[me.max].index =
358 flecs_component_ids_index_get();
359 }
360
361 return v;
362 }
363 };
364public:
365
366 enum_type() {
367 // Initialize/reset reflection data values to default state.
368 min = 0;
369 max = -1;
370 has_contiguous = true;
371 contiguous_until = 0;
372
373#if FLECS_CPP_ENUM_REFLECTION_SUPPORT
374 enum_reflection<E, reflection_init>::
375 template each_enum< static_cast<U>(enum_last<E>::value) >(*this);
376#endif
377 }
378
379 static enum_type<E>& get() {
380 static _::enum_type<E> instance;
381 return instance;
382 }
383
384 flecs::entity_t entity(E value) const {
385 int index = index_by_value(value);
386 if (index >= 0) {
387 return constants[index].id;
388 }
389 return 0;
390 }
391
392 void register_for_world(flecs::world_t *world, flecs::entity_t id) {
393#if !FLECS_CPP_ENUM_REFLECTION_SUPPORT
394 ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher")
395#endif
396
397 ecs_log_push();
398 ecs_cpp_enum_init(world, id, type<U>::id(world));
399
400 for (U v = 0; v < static_cast<U>(max + 1); v ++) {
401 if (constants[v].index) {
402 flecs::entity_t constant = ecs_cpp_enum_constant_register(world,
403 type<E>::id(world), 0, constants[v].name, &constants[v].value,
404 type<U>::id(world), sizeof(U));
405
406 flecs_component_ids_set(world, constants[v].index, constant);
407 }
408 }
409
410 ecs_log_pop();
411 }
412
413 int min;
414 int max;
415
416 // If enum constants start not-sparse, contiguous_until will be the index of
417 // the first sparse value, or end of the constants array
418 U contiguous_until;
419
420 // Compile-time generated count of enum constants.
421 static constexpr unsigned int constants_size =
422 enum_reflection<E, reflection_count>::
423 template each_enum< static_cast<U>(enum_last<E>::value) >();
424
425 // Constants array is sized to the number of found-constants, or 1
426 // to avoid 0-sized array.
427 #ifdef FLECS_CPP_ENUM_REFLECTION
428 enum_constant<U> constants[constants_size? constants_size: 1] = {};
429 bool has_contiguous;
430 #else
431 // If we're not using enum reflection, we cannot statically determine the
432 // upper bound of the enum, so use 128.
433 enum_constant<U> constants[128] = {};
434 bool has_contiguous = true; // Assume contiguous ids
435 #endif
436};
437
438template <typename E, if_t< is_enum<E>::value > = 0>
439inline static void init_enum(flecs::world_t *world, flecs::entity_t id) {
440 _::enum_type<E>::get().register_for_world(world, id);
441}
442
443template <typename E, if_not_t< is_enum<E>::value > = 0>
444inline static void init_enum(flecs::world_t*, flecs::entity_t) { }
445
446} // namespace _
447
449template <typename E>
450 struct enum_data {
451 using U = underlying_type_t<E>;
452
453 enum_data(flecs::world_t *world, _::enum_type<E>& impl)
454 : world_(world)
455 , impl_(impl) { }
456
464 bool is_valid(U value) {
465 int index = index_by_value(value);
466 if (index < 0) {
467 return false;
468 }
469 return impl_.constants[index].index != 0;
470 }
471
479 bool is_valid(E value) {
480 return is_valid(static_cast<U>(value));
481 }
482
489 int index_by_value(U value) const {
490 if (impl_.max < 0) {
491 return -1;
492 }
493
494 // Check if value is in contiguous lookup section
495 if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) {
496 return static_cast<int>(value);
497 }
498 U accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0;
499 for (int i = static_cast<int>(impl_.contiguous_until); i <= impl_.max; ++i) {
500 accumulator += impl_.constants[i].offset;
501 if (accumulator == value) {
502 return i;
503 }
504 }
505 return -1;
506 }
507
514 int index_by_value(E value) const {
515 return index_by_value(static_cast<U>(value));
516 }
517
518 int first() const {
519 return impl_.min;
520 }
521
522 int last() const {
523 return impl_.max;
524 }
525
526 int next(int cur) const {
527 return cur + 1;
528 }
529
530 flecs::entity entity() const;
531 flecs::entity entity(U value) const;
532 flecs::entity entity(E value) const;
533
540 #ifdef FLECS_CPP_NO_ENUM_REFLECTION
541 void register_constant(flecs::world_t *world, U v, flecs::entity_t e) {
542 if (v < 128) {
543 if (!impl_.constants[v].index) {
544 impl_.constants[v].index = flecs_component_ids_index_get();
545 }
546
547 flecs_component_ids_set(world, impl_.constants[v].index, e);
548
549 impl_.max ++;
550
551 if (impl_.contiguous_until <= v) {
552 impl_.contiguous_until = v + 1;
553 }
554 }
555 }
556 #endif
557
558 flecs::world_t *world_;
559 _::enum_type<E>& impl_;
560};
561
563template <typename E>
564 enum_data<E> enum_type(flecs::world_t *world) {
565 _::type<E>::id(world); // Ensure enum is registered
566 auto& ref = _::enum_type<E>::get();
567 return enum_data<E>(world, ref);
568}
569
570} // namespace flecs
ecs_assert
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:368
ecs_abort
#define ecs_abort(error_code,...)
Abort.
Definition log.h:359
flecs::component::constant
untyped_component & constant(const char *name, T value)
Add constant.
Definition untyped_component.inl:192
flecs::_
Int to enum.
Definition component.hpp:18
flecs::_::enum_constant_is_valid
constexpr bool enum_constant_is_valid()
Test if value is valid for enumeration.
Definition enum.hpp:153
flecs::_::enum_constant
Enumeration constant data.
Definition enum.hpp:279
flecs::_::enum_is_valid
Definition enum.hpp:166
flecs::_::enum_reflection
Provides utilities for enum reflection.
Definition enum.hpp:192
flecs::_::enum_reflection::each_enum_range
static constexpr U each_enum_range(U last_value, Args &... args)
Iterates over the range [Low, High] of enum values between Low and High.
Definition enum.hpp:212
flecs::_::enum_reflection::each_enum
static constexpr U each_enum(Args &... args)
Handles enum iteration for gathering reflection data.
Definition enum.hpp:266
flecs::_::enum_reflection::each_mask_range
static constexpr U each_mask_range(U last_value, Args &... args)
Iterates over the mask range (Low, High] of enum values between Low and High.
Definition enum.hpp:242
flecs::_::enum_type
Class that scans an enum for constants, extracts names & creates entities.
Definition enum.hpp:288
flecs::_::to_constant
Definition enum.hpp:48
flecs::_::type
Definition flecs.hpp:39
flecs::enum_data
Convenience type with enum reflection data.
Definition enum.hpp:450
flecs::enum_data::index_by_value
int index_by_value(E value) const
Finds the index into the constants array for an enum value, if one exists.
Definition enum.hpp:514
flecs::enum_data::is_valid
bool is_valid(U value)
Checks if a given integral value is a valid enum value.
Definition enum.hpp:464
flecs::enum_data::world_
flecs::world_t * world_
Manually register constant for enum.
Definition enum.hpp:558
flecs::enum_data::index_by_value
int index_by_value(U value) const
Finds the index into the constants array for a value, if one exists.
Definition enum.hpp:489
flecs::enum_data::is_valid
bool is_valid(E value)
Checks if a given enum value is valid.
Definition enum.hpp:479
flecs::enum_last
Definition enum.hpp:64
flecs::ref
Component reference.
Definition ref.hpp:85
flecs::world
The world.
Definition world.hpp:150
flecs::world::entity
flecs::entity entity(Args &&... args) const
Create an entity.