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