Flecs v3.2
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 <string.h>
10#include <limits>
11
12#define FLECS_ENUM_MAX(T) _::to_constant<T, 128>::value
13#define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1)
14
15#ifndef FLECS_CPP_ENUM_REFLECTION_SUPPORT
16#if !defined(__clang__) && defined(__GNUC__)
17#if __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 5)
18#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
19#else
20#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 0
21#endif
22#else
23#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
24#endif
25#endif
26
27namespace flecs {
28
30namespace _ {
31template <typename E, underlying_type_t<E> Value>
33#if defined(__clang__) && __clang_major__ >= 16
34 // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307
35 static constexpr E value = __builtin_bit_cast(E, Value);
36#else
37 static constexpr E value = static_cast<E>(Value);
38#endif
39};
40
41template <typename E, underlying_type_t<E> Value>
43}
44
46template <typename E>
47struct enum_data;
48
49template <typename E>
50static enum_data<E> enum_type(flecs::world_t *world);
51
52template <typename E>
53struct enum_last {
54 static constexpr E value = FLECS_ENUM_MAX(E);
55};
56
57/* Utility macro to override enum_last trait */
58#define FLECS_ENUM_LAST(T, Last)\
59 namespace flecs {\
60 template<>\
61 struct enum_last<T> {\
62 static constexpr T value = Last;\
63 };\
64 }
65
66namespace _ {
67
68#if INTPTR_MAX == INT64_MAX
69 #ifdef ECS_TARGET_MSVC
70 #if _MSC_VER >= 1929
71 #define ECS_SIZE_T_STR "unsigned __int64"
72 #else
73 #define ECS_SIZE_T_STR "unsigned int"
74 #endif
75 #elif defined(__clang__)
76 #define ECS_SIZE_T_STR "size_t"
77 #else
78 #ifdef ECS_TARGET_WINDOWS
79 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long long unsigned int"
80 #else
81 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long unsigned int"
82 #endif
83 #endif
84#else
85 #ifdef ECS_TARGET_MSVC
86 #if _MSC_VER >= 1929
87 #define ECS_SIZE_T_STR "unsigned __int32"
88 #else
89 #define ECS_SIZE_T_STR "unsigned int"
90 #endif
91 #elif defined(__clang__)
92 #define ECS_SIZE_T_STR "size_t"
93 #else
94 #ifdef ECS_TARGET_WINDOWS
95 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
96 #else
97 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
98 #endif
99 #endif
100#endif
101
102template <typename E>
103constexpr size_t enum_type_len() {
104 return ECS_FUNC_TYPE_LEN(, enum_type_len, ECS_FUNC_NAME)
105 - (sizeof(ECS_SIZE_T_STR) - 1u);
106}
107
112#if defined(ECS_TARGET_CLANG)
113#if ECS_CLANG_VERSION < 13
114template <typename E, E C>
115constexpr bool enum_constant_is_valid() {
116 return !((
117 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
118 enum_type_len<E>() + 6 /* ', C = ' */] >= '0') &&
119 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
120 enum_type_len<E>() + 6 /* ', C = ' */] <= '9')) ||
121 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
122 enum_type_len<E>() + 6 /* ', C = ' */] == '-'));
123}
124#else
125template <typename E, E C>
126constexpr bool enum_constant_is_valid() {
127 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
128 enum_type_len<E>() + 6 /* ', E C = ' */] != '(');
129}
130#endif
131#elif defined(ECS_TARGET_GNU)
132template <typename E, E C>
133constexpr bool enum_constant_is_valid() {
134 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constexpr bool, enum_constant_is_valid) +
135 enum_type_len<E>() + 8 /* ', E C = ' */] != '(');
136}
137#else
138/* Use different trick on MSVC, since it uses hexadecimal representation for
139 * invalid enum constants. We can leverage that msvc inserts a C-style cast
140 * into the name, and the location of its first character ('(') is known. */
141template <typename E, E C>
142constexpr bool enum_constant_is_valid() {
143 return ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
144 enum_type_len<E>() + 1] != '(';
145}
146#endif
147
148template <typename E, E C>
150 static constexpr bool value = enum_constant_is_valid<E, C>();
151};
152
154template <typename E, E C>
155static const char* enum_constant_to_name() {
156 static const size_t len = ECS_FUNC_TYPE_LEN(const char*, enum_constant_to_name, ECS_FUNC_NAME);
157 static char result[len + 1] = {};
158 return ecs_cpp_get_constant_name(
159 result, ECS_FUNC_NAME, string::length(ECS_FUNC_NAME),
160 ECS_FUNC_NAME_BACK);
161}
162
164template<typename T>
166 flecs::entity_t id;
167 T offset;
168};
169
180template <typename E, template<typename> class Handler>
182 template <E Value>
183 static constexpr underlying_type_t<E> to_int() {
184 return static_cast<underlying_type_t<E>>(Value);
185 }
186
187 template <underlying_type_t<E> Value>
188 static constexpr E from_int() {
190 }
191
192 template <E Value>
193 static constexpr underlying_type_t<E> is_not_0() {
194 return static_cast<underlying_type_t<E>>(Value != from_int<0>());
195 }
196
211 template <E Low, E High, typename... Args>
212 static constexpr underlying_type_t<E> each_enum_range(underlying_type_t<E> last_value, Args... args) {
213 return to_int<High>() - to_int<Low>() <= 1
214 ? to_int<High>() == to_int<Low>()
215 ? Handler<E>::template handle_constant<Low>(last_value, args...)
216 : Handler<E>::template handle_constant<High>(Handler<E>::template handle_constant<Low>(last_value, args...), args...)
217 : each_enum_range<from_int<(to_int<Low>()+to_int<High>()) / 2 + 1>(), High>(
218 each_enum_range<Low, from_int<(to_int<Low>()+to_int<High>()) / 2>()>(last_value, args...),
219 args...
220 );
221 }
222
237 template <E Low, E High, typename... Args>
238 static constexpr underlying_type_t<E> each_mask_range(underlying_type_t<E> last_value, Args... args) {
239 // If Low shares any bits with Current Flag, or if High is less than/equal to Low (and High isn't negative because max-flag signed)
240 return (to_int<Low>() & to_int<High>()) || (to_int<High>() <= to_int<Low>() && to_int<High>() != high_bit)
241 ? last_value
242 : Handler<E>::template handle_constant<High>(
243 each_mask_range<Low, from_int<((to_int<High>() >> 1) & ~high_bit)>()>(last_value, args...),
244 args...
245 );
246 }
247
260 template <E Value = FLECS_ENUM_MAX(E), typename... Args>
261 static constexpr underlying_type_t<E> each_enum(Args... args) {
262 return each_mask_range<Value, from_int<high_bit>()>(each_enum_range<from_int<0>(), Value>(0, args...), args...);
263 }
264
265 static const underlying_type_t<E> high_bit = static_cast<underlying_type_t<E>>(1) << (sizeof(underlying_type_t<E>) * 8 - 1);
266};
267
269template<typename E>
271private:
275 template<typename Enum>
276 struct reflection_count {
277 template <Enum Value, flecs::if_not_t< enum_constant_is_valid<Enum, Value>() > = 0>
278 static constexpr underlying_type_t<Enum> handle_constant(underlying_type_t<E> last_value) {
279 return last_value;
280 }
281
282 template <Enum Value, flecs::if_t< enum_constant_is_valid<Enum, Value>() > = 0>
283 static constexpr underlying_type_t<Enum> handle_constant(underlying_type_t<E> last_value) {
284 return 1 + last_value;
285 }
286 };
287public:
288 flecs::entity_t id;
289 int min;
290 int max;
291 bool has_contiguous;
292 // If enum constants start not-sparse, contiguous_until will be the index of the first sparse value, or end of the constants array
293 underlying_type_t<E> contiguous_until;
294 // Compile-time generated count of enum constants.
295 static constexpr unsigned int constants_size = enum_reflection<E, reflection_count>::template each_enum< enum_last<E>::value >();
296 // Constants array is sized to the number of found-constants, or 1 (to avoid 0-sized array)
297 enum_constant_data<underlying_type_t<E>> constants[constants_size? constants_size: 1];
298};
299
301template <typename E>
302struct enum_type {
303private:
312 template <typename Enum>
313 struct reflection_init {
314 template <Enum Value, flecs::if_not_t< enum_constant_is_valid<Enum, Value>() > = 0>
315 static underlying_type_t<Enum> handle_constant(underlying_type_t<Enum> last_value, flecs::world_t*) {
316 // Search for constant failed. Pass last valid value through.
317 return last_value;
318 }
319
320 template <Enum Value, flecs::if_t< enum_constant_is_valid<Enum, Value>() > = 0>
321 static underlying_type_t<Enum> handle_constant(underlying_type_t<Enum> last_value, flecs::world_t *world) {
322 // Constant is valid, so fill reflection data.
323 auto v = enum_reflection<E, reflection_init>::template to_int<Value>();
324 const char *name = enum_constant_to_name<Enum, Value>();
325
326 ++enum_type<Enum>::data.max; // Increment cursor as we build constants array.
327
328 // If the enum was previously contiguous, and continues to be through the current value...
329 if (enum_type<Enum>::data.has_contiguous && static_cast<underlying_type_t<Enum>>(enum_type<Enum>::data.max) == v && enum_type<Enum>::data.contiguous_until == v) {
330 ++enum_type<Enum>::data.contiguous_until;
331 }
332 // else, if the enum was never contiguous and hasn't been set as not contiguous...
333 else if (!enum_type<Enum>::data.contiguous_until && enum_type<Enum>::data.has_contiguous) {
334 enum_type<Enum>::data.has_contiguous = false;
335 }
336
337 ecs_assert(!(last_value > 0 && v < std::numeric_limits<underlying_type_t<Enum>>::min() + last_value), ECS_UNSUPPORTED,
338 "Signed integer enums causes integer overflow when recording offset from high positive to"
339 " low negative. Consider using unsigned integers as underlying type.");
340 enum_type<Enum>::data.constants[enum_type<Enum>::data.max].offset = v - last_value;
341 enum_type<Enum>::data.constants[enum_type<Enum>::data.max].id = ecs_cpp_enum_constant_register(
342 world, enum_type<Enum>::data.id, 0, name, static_cast<int32_t>(v));
343 return v;
344 }
345 };
346public:
347
348 static enum_data_impl<E> data;
349
350 static enum_type<E>& get() {
351 static _::enum_type<E> instance;
352 return instance;
353 }
354
355 flecs::entity_t entity(E value) const {
356 int index = index_by_value(value);
357 if (index >= 0) {
358 return data.constants[index].id;
359 }
360 return 0;
361 }
362
363 void init(flecs::world_t *world, flecs::entity_t id) {
364#if !FLECS_CPP_ENUM_REFLECTION_SUPPORT
365 ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher")
366#endif
367 // Initialize/reset reflection data values to default state.
368 data.min = 0;
369 data.max = -1;
370 data.has_contiguous = true;
371 data.contiguous_until = 0;
372
373 ecs_log_push();
374 ecs_cpp_enum_init(world, id);
375 data.id = id;
376
377 // Generate reflection data
378 enum_reflection<E, reflection_init>::template each_enum< enum_last<E>::value >(world);
379 ecs_log_pop();
380 }
381
382};
383
384template <typename E>
386
387template <typename E, if_t< is_enum<E>::value > = 0>
388inline static void init_enum(flecs::world_t *world, flecs::entity_t id) {
389 _::enum_type<E>::get().init(world, id);
390}
391
392template <typename E, if_not_t< is_enum<E>::value > = 0>
393inline static void init_enum(flecs::world_t*, flecs::entity_t) { }
394
395} // namespace _
396
398template <typename E>
399struct enum_data {
400 enum_data(flecs::world_t *world, _::enum_data_impl<E>& impl)
401 : world_(world)
402 , impl_(impl) { }
403
411 bool is_valid(underlying_type_t<E> value) {
412 int index = index_by_value(value);
413 if (index < 0) {
414 return false;
415 }
416 return impl_.constants[index].id != 0;
417 }
418
426 bool is_valid(E value) {
427 return is_valid(static_cast<underlying_type_t<E>>(value));
428 }
429
436 int index_by_value(underlying_type_t<E> value) const {
437 if (!impl_.max) {
438 return -1;
439 }
440 // Check if value is in contiguous lookup section
441 if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) {
442 return static_cast<int>(value);
443 }
444 underlying_type_t<E> accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0;
445 for (int i = static_cast<int>(impl_.contiguous_until); i <= impl_.max; ++i) {
446 accumulator += impl_.constants[i].offset;
447 if (accumulator == value) {
448 return i;
449 }
450 }
451 return -1;
452 }
453
460 int index_by_value(E value) const {
461 return index_by_value(static_cast<underlying_type_t<E>>(value));
462 }
463
464 int first() const {
465 return impl_.min;
466 }
467
468 int last() const {
469 return impl_.max;
470 }
471
472 int next(int cur) const {
473 return cur + 1;
474 }
475
476 flecs::entity entity() const;
477 flecs::entity entity(underlying_type_t<E> value) const;
478 flecs::entity entity(E value) const;
479
480 flecs::world_t *world_;
481 _::enum_data_impl<E>& impl_;
482};
483
485template <typename E>
486enum_data<E> enum_type(flecs::world_t *world) {
487 _::cpp_type<E>::id(world); // Ensure enum is registered
488 auto& ref = _::enum_type<E>::get();
489 return enum_data<E>(world, ref.data);
490}
491
492} // namespace flecs
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:351
#define ecs_abort(error_code,...)
Abort.
Definition log.h:342
constexpr bool enum_constant_is_valid()
Test if value is valid for enumeration.
Definition enum.hpp:142
Enumeration constant data.
Definition enum.hpp:165
Enumeration type data.
Definition enum.hpp:270
Provides utilities for enum reflection.
Definition enum.hpp:181
static constexpr underlying_type_t< E > each_enum(Args... args)
Handles enum iteration for gathering reflection data.
Definition enum.hpp:261
static constexpr underlying_type_t< E > each_mask_range(underlying_type_t< E > last_value, Args... args)
Iterates over the mask range (Low, High] of enum values between Low and High.
Definition enum.hpp:238
static constexpr underlying_type_t< E > each_enum_range(underlying_type_t< E > last_value, Args... args)
Iterates over the range [Low, High] of enum values between Low and High.
Definition enum.hpp:212
Class that scans an enum for constants, extracts names & creates entities.
Definition enum.hpp:302
Entity.
Definition entity.hpp:30
Convenience type with enum reflection data.
Definition enum.hpp:399
bool is_valid(underlying_type_t< E > value)
Checks if a given integral value is a valid enum value.
Definition enum.hpp:411
int index_by_value(underlying_type_t< E > value) const
Finds the index into the constants array for a value, if one exists.
Definition enum.hpp:436
int index_by_value(E value) const
Finds the index into the constants array for an enum value, if one exists.
Definition enum.hpp:460
bool is_valid(E value)
Checks if a given enum value is valid.
Definition enum.hpp:426
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
Component reference.
Definition ref.hpp:23
The world.
Definition world.hpp:132