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
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#ifndef FLECS_CPP_ENUM_REFLECTION_SUPPORT
18#if !defined(__clang__) && defined(__GNUC__)
19#if __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 5)
20#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
21#else
22#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 0
23#endif
24#else
25#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
26#endif
27#endif
28
29#if defined(__clang__) && __clang_major__ >= 16
30// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307
31#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v)
32#elif defined(__GNUC__) && __GNUC__ > 10
33#define flecs_enum_cast(T, v) __builtin_bit_cast(T, v)
34#else
35#define flecs_enum_cast(T, v) static_cast<T>(v)
36#endif
37
38namespace flecs {
39
41namespace _ {
42template <typename E, underlying_type_t<E> Value>
44 static constexpr E value = flecs_enum_cast(E, Value);
45};
46
47template <typename E, underlying_type_t<E> Value>
49}
50
52template <typename E>
53struct enum_data;
54
55template <typename E>
56static enum_data<E> enum_type(flecs::world_t *world);
57
58template <typename E>
59struct enum_last {
60 static constexpr E value = FLECS_ENUM_MAX(E);
61};
62
63/* Utility macro to override enum_last trait */
64#define FLECS_ENUM_LAST(T, Last)\
65 namespace flecs {\
66 template<>\
67 struct enum_last<T> {\
68 static constexpr T value = Last;\
69 };\
70 }
71
72namespace _ {
73
74#if INTPTR_MAX == INT64_MAX
75 #ifdef ECS_TARGET_MSVC
76 #if _MSC_VER >= 1929
77 #define ECS_SIZE_T_STR "unsigned __int64"
78 #else
79 #define ECS_SIZE_T_STR "unsigned int"
80 #endif
81 #elif defined(__clang__)
82 #define ECS_SIZE_T_STR "size_t"
83 #else
84 #ifdef ECS_TARGET_WINDOWS
85 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long long unsigned int"
86 #else
87 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long unsigned int"
88 #endif
89 #endif
90#else
91 #ifdef ECS_TARGET_MSVC
92 #if _MSC_VER >= 1929
93 #define ECS_SIZE_T_STR "unsigned __int32"
94 #else
95 #define ECS_SIZE_T_STR "unsigned int"
96 #endif
97 #elif defined(__clang__)
98 #define ECS_SIZE_T_STR "size_t"
99 #else
100 #ifdef ECS_TARGET_WINDOWS
101 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
102 #else
103 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
104 #endif
105 #endif
106#endif
107
108template <typename E>
109constexpr size_t enum_type_len() {
110 return ECS_FUNC_TYPE_LEN(, enum_type_len, ECS_FUNC_NAME)
111 - (sizeof(ECS_SIZE_T_STR) - 1u);
112}
113
118#if defined(ECS_TARGET_CLANG)
119#if ECS_CLANG_VERSION < 13
120template <typename E, E C>
121constexpr bool enum_constant_is_valid() {
122 return !((
123 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
124 enum_type_len<E>() + 6 /* ', C = ' */] >= '0') &&
125 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
126 enum_type_len<E>() + 6 /* ', C = ' */] <= '9')) ||
127 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
128 enum_type_len<E>() + 6 /* ', C = ' */] == '-'));
129}
130#else
131template <typename E, E C>
132constexpr bool enum_constant_is_valid() {
133 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
134 enum_type_len<E>() + 6 /* ', E C = ' */] != '(');
135}
136#endif
137#elif defined(ECS_TARGET_GNU)
138template <typename E, E C>
139constexpr bool enum_constant_is_valid() {
140 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constexpr bool, enum_constant_is_valid) +
141 enum_type_len<E>() + 8 /* ', E C = ' */] != '(');
142}
143#else
144/* Use different trick on MSVC, since it uses hexadecimal representation for
145 * invalid enum constants. We can leverage that msvc inserts a C-style cast
146 * into the name, and the location of its first character ('(') is known. */
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>() + 1] != '(';
151}
152#endif
153
154/* Without this wrapper __builtin_bit_cast doesn't work */
155template <typename E, underlying_type_t<E> C>
156constexpr bool enum_constant_is_valid_wrap() {
157 return enum_constant_is_valid<E, flecs_enum_cast(E, C)>();
158}
159
160template <typename E, E C>
162 static constexpr bool value = enum_constant_is_valid<E, C>();
163};
164
166template <typename E, E C>
167static const char* enum_constant_to_name() {
168 static const size_t len = ECS_FUNC_TYPE_LEN(const char*, enum_constant_to_name, ECS_FUNC_NAME);
169 static char result[len + 1] = {};
170 return ecs_cpp_get_constant_name(
171 result, ECS_FUNC_NAME, string::length(ECS_FUNC_NAME),
172 ECS_FUNC_NAME_BACK);
173}
174
176template<typename T>
178 int32_t index; // Global index used to obtain world local entity id
179 T offset;
180};
181
192template <typename E, typename Handler>
194 using U = underlying_type_t<E>;
195
210 template <U Low, U High, typename... Args>
211 static constexpr U each_enum_range(U last_value, Args... args) {
212 return High - Low <= 1
213 ? High == Low
214 ? Handler::template handle_constant<Low>(last_value, args...)
215 : Handler::template handle_constant<High>(Handler::template handle_constant<Low>(last_value, args...), args...)
216 : each_enum_range<(Low + High) / 2 + 1, High>(
217 each_enum_range<Low, (Low + High) / 2>(last_value, args...),
218 args...
219 );
220 }
221
236 template <U Low, U High, typename... Args>
237 static constexpr U each_mask_range(U last_value, Args... args) {
238 // 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)
239 return (Low & High) || (High <= Low && High != high_bit)
240 ? last_value
241 : Handler::template handle_constant<High>(
242 each_mask_range<Low, ((High >> 1) & ~high_bit)>(last_value, args...),
243 args...
244 );
245 }
246
259 template <U Value = static_cast<U>(FLECS_ENUM_MAX(E)), typename... Args>
260 static constexpr U each_enum(Args... args) {
261 return each_mask_range<Value, high_bit>(each_enum_range<0, Value>(0, args...), args...);
262 }
263 /* to avoid warnings with bit manipulation, calculate the high bit with an
264 unsigned type of the same size: */
265 using UU = typename std::make_unsigned<U>::type;
266 static const U high_bit = static_cast<U>(static_cast<UU>(1) << (sizeof(UU) * 8 - 1));
267};
268
270template<typename E>
272private:
273 using U = underlying_type_t<E>;
274
278 struct reflection_count {
279 template <U Value, flecs::if_not_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
280 static constexpr U handle_constant(U last_value) {
281 return last_value;
282 }
283
284 template <U Value, flecs::if_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
285 static constexpr U handle_constant(U last_value) {
286 return 1 + last_value;
287 }
288 };
289
290public:
291 int min;
292 int max;
293 bool has_contiguous;
294 // If enum constants start not-sparse, contiguous_until will be the index of the first sparse value, or end of the constants array
295 U contiguous_until;
296 // Compile-time generated count of enum constants.
297 static constexpr unsigned int constants_size = enum_reflection<E, reflection_count>::template each_enum< static_cast<U>(enum_last<E>::value) >();
298 // Constants array is sized to the number of found-constants, or 1 (to avoid 0-sized array)
299 enum_constant_data<U> constants[constants_size? constants_size: 1];
300};
301
303template <typename E>
304struct enum_type {
305private:
306 using U = underlying_type_t<E>;
307
314 struct reflection_init {
315 template <U Value, flecs::if_not_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
316 static U handle_constant(U last_value, flecs::world_t*) {
317 // Search for constant failed. Pass last valid value through.
318 return last_value;
319 }
320
321 template <U Value, flecs::if_t< enum_constant_is_valid_wrap<E, Value>() > = 0>
322 static U handle_constant(U last_value, flecs::world_t *world) {
323 // Constant is valid, so fill reflection data.
324 auto v = Value;
325 const char *name = enum_constant_to_name<E, flecs_enum_cast(E, Value)>();
326
327 ++enum_type<E>::data.max; // Increment cursor as we build constants array.
328
329 // If the enum was previously contiguous, and continues to be through the current value...
330 if (enum_type<E>::data.has_contiguous && static_cast<U>(enum_type<E>::data.max) == v && enum_type<E>::data.contiguous_until == v) {
331 ++enum_type<E>::data.contiguous_until;
332 }
333 // else, if the enum was never contiguous and hasn't been set as not contiguous...
334 else if (!enum_type<E>::data.contiguous_until && enum_type<E>::data.has_contiguous) {
335 enum_type<E>::data.has_contiguous = false;
336 }
337
338 ecs_assert(!(last_value > 0 && v < std::numeric_limits<U>::min() + last_value), ECS_UNSUPPORTED,
339 "Signed integer enums causes integer overflow when recording offset from high positive to"
340 " low negative. Consider using unsigned integers as underlying type.");
341 enum_type<E>::data.constants[enum_type<E>::data.max].offset = v - last_value;
342 if (!enum_type<E>::data.constants[enum_type<E>::data.max].index) {
343 enum_type<E>::data.constants[enum_type<E>::data.max].index =
344 flecs_component_ids_index_get();
345 }
346
347 flecs::entity_t constant = ecs_cpp_enum_constant_register(
348 world, type<E>::id(world), 0, name, &v, type<U>::id(world), sizeof(U));
349 flecs_component_ids_set(world,
350 enum_type<E>::data.constants[enum_type<E>::data.max].index,
351 constant);
352
353 return v;
354 }
355 };
356public:
357
358 static enum_data_impl<E> data;
359
360 static enum_type<E>& get() {
361 static _::enum_type<E> instance;
362 return instance;
363 }
364
365 flecs::entity_t entity(E value) const {
366 int index = index_by_value(value);
367 if (index >= 0) {
368 return data.constants[index].id;
369 }
370 return 0;
371 }
372
373 void init(flecs::world_t *world, flecs::entity_t id) {
374#if !FLECS_CPP_ENUM_REFLECTION_SUPPORT
375 ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher")
376#endif
377 // Initialize/reset reflection data values to default state.
378 data.min = 0;
379 data.max = -1;
380 data.has_contiguous = true;
381 data.contiguous_until = 0;
382
383 ecs_log_push();
384 ecs_cpp_enum_init(world, id, type<U>::id(world));
385 // data.id = id;
386
387 // Generate reflection data
389 ecs_log_pop();
390 }
391};
392
393template <typename E>
395
396template <typename E, if_t< is_enum<E>::value > = 0>
397inline static void init_enum(flecs::world_t *world, flecs::entity_t id) {
398 _::enum_type<E>::get().init(world, id);
399}
400
401template <typename E, if_not_t< is_enum<E>::value > = 0>
402inline static void init_enum(flecs::world_t*, flecs::entity_t) { }
403
404} // namespace _
405
407template <typename E>
408struct enum_data {
409 using U = underlying_type_t<E>;
410
411 enum_data(flecs::world_t *world, _::enum_data_impl<E>& impl)
412 : world_(world)
413 , impl_(impl) { }
414
422 bool is_valid(U value) {
423 int index = index_by_value(value);
424 if (index < 0) {
425 return false;
426 }
427 return impl_.constants[index].index != 0;
428 }
429
437 bool is_valid(E value) {
438 return is_valid(static_cast<U>(value));
439 }
440
447 int index_by_value(U value) const {
448 if (impl_.max < 0) {
449 return -1;
450 }
451 // Check if value is in contiguous lookup section
452 if (impl_.has_contiguous && value < impl_.contiguous_until && value >= 0) {
453 return static_cast<int>(value);
454 }
455 U accumulator = impl_.contiguous_until? impl_.contiguous_until - 1: 0;
456 for (int i = static_cast<int>(impl_.contiguous_until); i <= impl_.max; ++i) {
457 accumulator += impl_.constants[i].offset;
458 if (accumulator == value) {
459 return i;
460 }
461 }
462 return -1;
463 }
464
471 int index_by_value(E value) const {
472 return index_by_value(static_cast<U>(value));
473 }
474
475 int first() const {
476 return impl_.min;
477 }
478
479 int last() const {
480 return impl_.max;
481 }
482
483 int next(int cur) const {
484 return cur + 1;
485 }
486
487 flecs::entity entity() const;
488 flecs::entity entity(U value) const;
489 flecs::entity entity(E value) const;
490
491 flecs::world_t *world_;
492 _::enum_data_impl<E>& impl_;
493};
494
496template <typename E>
497enum_data<E> enum_type(flecs::world_t *world) {
498 _::type<E>::id(world); // Ensure enum is registered
499 auto& ref = _::enum_type<E>::get();
500 return enum_data<E>(world, ref.data);
501}
502
503} // namespace flecs
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:368
#define ecs_abort(error_code,...)
Abort.
Definition log.h:359
constexpr bool enum_constant_is_valid()
Test if value is valid for enumeration.
Definition enum.hpp:148
Enumeration constant data.
Definition enum.hpp:177
Enumeration type data.
Definition enum.hpp:271
Provides utilities for enum reflection.
Definition enum.hpp:193
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:237
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:211
static constexpr U each_enum(Args... args)
Handles enum iteration for gathering reflection data.
Definition enum.hpp:260
Class that scans an enum for constants, extracts names & creates entities.
Definition enum.hpp:304
Entity.
Definition entity.hpp:30
Convenience type with enum reflection data.
Definition enum.hpp:408
int index_by_value(E value) const
Finds the index into the constants array for an enum value, if one exists.
Definition enum.hpp:471
bool is_valid(U value)
Checks if a given integral value is a valid enum value.
Definition enum.hpp:422
int index_by_value(U value) const
Finds the index into the constants array for a value, if one exists.
Definition enum.hpp:447
bool is_valid(E value)
Checks if a given enum value is valid.
Definition enum.hpp:437
Component reference.
Definition ref.hpp:23
The world.
Definition world.hpp:137