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 <string.h>
10#include <limits>
11
12// 126, so that FLECS_ENUM_MAX_COUNT is 127 which is the largest value
13// representable by an int8_t.
14#define FLECS_ENUM_MAX(T) _::to_constant<T, 126>::value
15#define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1)
16
17// Flag to turn off enum reflection
18#ifdef FLECS_CPP_NO_ENUM_REFLECTION
19#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 0
20#endif
21
22// Test if we're using a compiler that supports the required features
23#ifndef FLECS_CPP_ENUM_REFLECTION_SUPPORT
24#if !defined(__clang__) && defined(__GNUC__)
25#if __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 5)
26#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
27#else
28#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 0
29#endif
30#else
31#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
32#endif
33#endif
34
35#if defined(__clang__) && __clang_major__ >= 16
36// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307
37#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v)
38#elif defined(__GNUC__) && __GNUC__ > 10
39#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v)
40#else
41#define flecs_enum_cast(T, v) static_cast<T>(v)
42#endif
43
44namespace flecs {
45
47namespace _ {
48template <typename E, underlying_type_t<E> Value>
49 struct 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
61template <typename E>
62static enum_data<E> enum_type(flecs::world_t *world);
63
64template <typename E>
65 struct enum_last {
66 static constexpr E value = FLECS_ENUM_MAX(E);
67};
68
69/* Utility macro to override enum_last trait */
70#define FLECS_ENUM_LAST(T, Last)\
71 namespace flecs {\
72 template<>\
73 struct enum_last<T> {\
74 static constexpr T value = Last;\
75 };\
76 }
77
78namespace _ {
79
80#if INTPTR_MAX == INT64_MAX
81 #ifdef ECS_TARGET_MSVC
82 #if _MSC_VER >= 1929
83 #define ECS_SIZE_T_STR "unsigned __int64"
84 #else
85 #define ECS_SIZE_T_STR "unsigned int"
86 #endif
87 #elif defined(__clang__)
88 #define ECS_SIZE_T_STR "size_t"
89 #else
90 #ifdef ECS_TARGET_WINDOWS
91 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long long unsigned int"
92 #else
93 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long unsigned int"
94 #endif
95 #endif
96#else
97 #ifdef ECS_TARGET_MSVC
98 #if _MSC_VER >= 1929
99 #define ECS_SIZE_T_STR "unsigned __int32"
100 #else
101 #define ECS_SIZE_T_STR "unsigned int"
102 #endif
103 #elif defined(__clang__)
104 #define ECS_SIZE_T_STR "size_t"
105 #else
106 #ifdef ECS_TARGET_WINDOWS
107 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
108 #else
109 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
110 #endif
111 #endif
112#endif
113
114template <typename E>
115constexpr size_t enum_type_len() {
116 return ECS_FUNC_TYPE_LEN(, enum_type_len, ECS_FUNC_NAME)
117 - (sizeof(ECS_SIZE_T_STR) - 1u);
118}
119
124#if defined(ECS_TARGET_CLANG)
125#if ECS_CLANG_VERSION < 13
126template <typename E, E C>
127constexpr bool enum_constant_is_valid() {
128 return !((
129 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
130 enum_type_len<E>() + 6 /* ', C = ' */] >= '0') &&
131 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
132 enum_type_len<E>() + 6 /* ', C = ' */] <= '9')) ||
133 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
134 enum_type_len<E>() + 6 /* ', C = ' */] == '-'));
135}
136#else
137template <typename E, E C>
138constexpr bool enum_constant_is_valid() {
139 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
140 enum_type_len<E>() + 6 /* ', E C = ' */] != '(');
141}
142#endif
143#elif defined(ECS_TARGET_GNU)
144template <typename E, E C>
145constexpr bool enum_constant_is_valid() {
146 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constexpr bool, enum_constant_is_valid) +
147 enum_type_len<E>() + 8 /* ', E C = ' */] != '(');
148}
149#else
150/* Use different trick on MSVC, since it uses hexadecimal representation for
151 * invalid enum constants. We can leverage that msvc inserts a C-style cast
152 * into the name, and the location of its first character ('(') is known. */
153template <typename E, E C>
154 constexpr bool enum_constant_is_valid() {
155 return ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
156 enum_type_len<E>() + 1] != '(';
157}
158#endif
159
160/* Without this wrapper __builtin_bit_cast doesn't work */
161template <typename E, underlying_type_t<E> C>
162constexpr bool enum_constant_is_valid_wrap() {
163 return enum_constant_is_valid<E, flecs_enum_cast(E, C)>();
164}
165
166template <typename E, E C>
167 struct enum_is_valid {
168 static constexpr bool value = enum_constant_is_valid<E, C>();
169};
170
172template <typename E, E C>
173static const char* enum_constant_to_name() {
174 static const size_t len = ECS_FUNC_TYPE_LEN(
175 const char*, enum_constant_to_name, ECS_FUNC_NAME);
176 static char result[len + 1] = {};
177 return ecs_cpp_get_constant_name(
178 result, ECS_FUNC_NAME, string::length(ECS_FUNC_NAME),
179 ECS_FUNC_NAME_BACK);
180}
181
192template <typename E, typename Handler>
193 struct enum_reflection {
194 using U = underlying_type_t<E>;
195
212 template <U Low, U High, typename... Args>
213 static constexpr U each_enum_range(U last_value, Args&... args) {
214 return High - Low <= 1
215 ? High == Low
216 ? Handler::template handle_constant<Low>(last_value, args...)
217 : Handler::template handle_constant<High>(
218 Handler::template handle_constant<Low>(last_value, args...),
219 args...)
220 : each_enum_range<(Low + High) / 2 + 1, High>(
221 each_enum_range<Low, (Low + High) / 2>(last_value, args...),
222 args...
223 );
224 }
225
242 template <U Low, U High, typename... Args>
243 static constexpr U each_mask_range(U last_value, Args&... args) {
244 // If Low shares any bits with Current Flag, or if High is less
245 // than/equal to Low (and High isn't negative because max-flag signed)
246 return (Low & High) || (High <= Low && High != high_bit)
247 ? last_value
248 : Handler::template handle_constant<High>(
249 each_mask_range<Low, ((High >> 1) & ~high_bit)>(last_value, args...),
250 args...
251 );
252 }
253
266 template <U Value = static_cast<U>(FLECS_ENUM_MAX(E)), typename... Args>
267 static constexpr U each_enum(Args&... args) {
268 return each_mask_range<Value, high_bit>(
269 each_enum_range<0, Value>(0, args...), args...);
270 }
271 /* to avoid warnings with bit manipulation, calculate the high bit with an
272 unsigned type of the same size: */
273 using UU = typename std::make_unsigned<U>::type;
274 static const U high_bit =
275 static_cast<U>(static_cast<UU>(1) << (sizeof(UU) * 8 - 1));
276};
277
279template<typename T>
280 struct enum_constant {
281 int32_t index; // Global index used to obtain world local entity id
282 T value;
283 T offset;
284 const char *name;
285};
286
288template <typename E>
289 struct enum_type {
290private:
291 using This = enum_type<E>;
292 using U = underlying_type_t<E>;
293
297 struct reflection_count {
298 template <U Value,
299 flecs::if_not_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
300 static constexpr U handle_constant(U last_value) {
301 return last_value;
302 }
303
304 template <U Value,
305 flecs::if_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
306 static constexpr U handle_constant(U last_value) {
307 return 1 + last_value;
308 }
309 };
310
318 struct reflection_init {
319 template <U Value,
320 flecs::if_not_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
321 static U handle_constant(U last_value, This&) {
322 // Search for constant failed. Pass last valid value through.
323 return last_value;
324 }
325
326 template <U Value,
327 flecs::if_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
328 static U handle_constant(U last_value, This& me) {
329 // Constant is valid, so fill reflection data.
330 auto v = Value;
331 const char *name = enum_constant_to_name<E, flecs_enum_cast(E, Value)>();
332
333 ++me.max; // Increment cursor as we build constants array.
334
335 // If the enum was previously contiguous, and continues to be
336 // through the current value...
337 if (me.has_contiguous && static_cast<U>(me.max) == v && me.contiguous_until == v) {
338 ++me.contiguous_until;
339 }
340
341 // else, if the enum was never contiguous and hasn't been set as not
342 // contiguous...
343 else if (!me.contiguous_until && me.has_contiguous) {
344 me.has_contiguous = false;
345 }
346
347 ecs_assert(!(last_value > 0 &&
348 v < std::numeric_limits<U>::min() + last_value),
349 ECS_UNSUPPORTED,
350 "Signed integer enums causes integer overflow when recording "
351 "offset from high positive to low negative. Consider using "
352 "unsigned integers as underlying type.");
353
354 me.constants[me.max].value = v;
355 me.constants[me.max].offset = v - last_value;
356 me.constants[me.max].name = name;
357 if (!me.constants[me.max].index) {
358 me.constants[me.max].index =
359 flecs_component_ids_index_get();
360 }
361
362 return v;
363 }
364 };
365public:
366
367 enum_type() {
368 // Initialize/reset reflection data values to default state.
369 min = 0;
370 max = -1;
371 has_contiguous = true;
372 contiguous_until = 0;
373
374 enum_reflection<E, reflection_init>::
375 template each_enum< static_cast<U>(enum_last<E>::value) >(*this);
376 }
377
378 static enum_type<E>& get() {
379 static _::enum_type<E> instance;
380 return instance;
381 }
382
383 flecs::entity_t entity(E value) const {
384 int index = index_by_value(value);
385 if (index >= 0) {
386 return constants[index].id;
387 }
388 return 0;
389 }
390
391 void register_for_world(flecs::world_t *world, flecs::entity_t id) {
392#if !FLECS_CPP_ENUM_REFLECTION_SUPPORT
393 ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher")
394#endif
395
396 ecs_log_push();
397 ecs_cpp_enum_init(world, id, type<U>::id(world));
398
399 for (U v = 0; v < static_cast<U>(max + 1); v ++) {
400 if (constants[v].index) {
401 flecs::entity_t constant = ecs_cpp_enum_constant_register(world,
402 type<E>::id(world), 0, constants[v].name, &constants[v].value,
403 type<U>::id(world), sizeof(U));
404
405 flecs_component_ids_set(world, constants[v].index, constant);
406 }
407 }
408
409 ecs_log_pop();
410 }
411
412 int min;
413 int max;
414 bool has_contiguous;
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 enum_constant<U> constants[constants_size? constants_size: 1] = {};
428};
429
430template <typename E, if_t< is_enum<E>::value > = 0>
431inline static void init_enum(flecs::world_t *world, flecs::entity_t id) {
432 _::enum_type<E>::get().register_for_world(world, id);
433}
434
435template <typename E, if_not_t< is_enum<E>::value > = 0>
436inline static void init_enum(flecs::world_t*, flecs::entity_t) { }
437
438} // namespace _
439
441template <typename E>
442 struct enum_data {
443 using U = underlying_type_t<E>;
444
445 enum_data(flecs::world_t *world, _::enum_type<E>& impl)
446 : world_(world)
447 , impl_(impl) { }
448
456 bool is_valid(U value) {
457 int index = index_by_value(value);
458 if (index < 0) {
459 return false;
460 }
461 return impl_.constants[index].index != 0;
462 }
463
471 bool is_valid(E value) {
472 return is_valid(static_cast<U>(value));
473 }
474
481 int index_by_value(U value) const {
482 if (impl_.max < 0) {
483 return -1;
484 }
485 // Check if value is in contiguous lookup section
486 if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) {
487 return static_cast<int>(value);
488 }
489 U accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0;
490 for (int i = static_cast<int>(impl_.contiguous_until); i <= impl_.max; ++i) {
491 accumulator += impl_.constants[i].offset;
492 if (accumulator == value) {
493 return i;
494 }
495 }
496 return -1;
497 }
498
505 int index_by_value(E value) const {
506 return index_by_value(static_cast<U>(value));
507 }
508
509 int first() const {
510 return impl_.min;
511 }
512
513 int last() const {
514 return impl_.max;
515 }
516
517 int next(int cur) const {
518 return cur + 1;
519 }
520
521 flecs::entity entity() const;
522 flecs::entity entity(U value) const;
523 flecs::entity entity(E value) const;
524
525 flecs::world_t *world_;
526 _::enum_type<E>& impl_;
527};
528
530template <typename E>
531 enum_data<E> enum_type(flecs::world_t *world) {
532 _::type<E>::id(world); // Ensure enum is registered
533 auto& ref = _::enum_type<E>::get();
534 return enum_data<E>(world, ref);
535}
536
537} // 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:21
flecs::_::enum_constant_is_valid
constexpr bool enum_constant_is_valid()
Test if value is valid for enumeration.
Definition enum.hpp:154
flecs::_::enum_constant
Enumeration constant data.
Definition enum.hpp:280
flecs::_::enum_is_valid
Definition enum.hpp:167
flecs::_::enum_reflection
Provides utilities for enum reflection.
Definition enum.hpp:193
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:213
flecs::_::enum_reflection::each_enum
static constexpr U each_enum(Args &... args)
Handles enum iteration for gathering reflection data.
Definition enum.hpp:267
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:243
flecs::_::enum_type
Class that scans an enum for constants, extracts names & creates entities.
Definition enum.hpp:289
flecs::_::to_constant
Definition enum.hpp:49
flecs::_::type
Definition flecs.hpp:39
flecs::enum_data
Convenience type with enum reflection data.
Definition enum.hpp:442
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:505
flecs::enum_data::is_valid
bool is_valid(U value)
Checks if a given integral value is a valid enum value.
Definition enum.hpp:456
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:481
flecs::enum_data::is_valid
bool is_valid(E value)
Checks if a given enum value is valid.
Definition enum.hpp:471
flecs::enum_last
Definition enum.hpp:65
flecs::ref
Component reference.
Definition ref.hpp:85
flecs::world
The world.
Definition world.hpp:137
flecs::world::entity
flecs::entity entity(Args &&... args) const
Create an entity.