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
11#define FLECS_ENUM_MAX(T) _::to_constant<T, 128>::value
12#define FLECS_ENUM_MAX_COUNT (FLECS_ENUM_MAX(int) + 1)
13
14#ifndef FLECS_CPP_ENUM_REFLECTION_SUPPORT
15#if !defined(__clang__) && defined(__GNUC__)
16#if __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 5)
17#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
18#else
19#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 0
20#endif
21#else
22#define FLECS_CPP_ENUM_REFLECTION_SUPPORT 1
23#endif
24#endif
25
26namespace flecs {
27
29namespace _ {
30template <typename E, int Value>
32#if defined(__clang__) && __clang_major__ >= 16
33 // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307
34 static constexpr E value = __builtin_bit_cast(E, Value);
35#else
36 static constexpr E value = static_cast<E>(Value);
37#endif
38};
39
40template <typename E, int Value>
42}
43
45template <typename E>
46struct enum_data;
47
48template <typename E>
49static enum_data<E> enum_type(flecs::world_t *world);
50
51template <typename E>
52struct enum_last {
53 static constexpr E value = FLECS_ENUM_MAX(E);
54};
55
56/* Utility macro to override enum_last trait */
57#define FLECS_ENUM_LAST(T, Last)\
58 namespace flecs {\
59 template<>\
60 struct enum_last<T> {\
61 static constexpr T value = Last;\
62 };\
63 }
64
65namespace _ {
66
67#if INTPTR_MAX == INT64_MAX
68 #ifdef ECS_TARGET_MSVC
69 #if _MSC_VER >= 1930
70 #define ECS_SIZE_T_STR "unsigned __int64"
71 #else
72 #define ECS_SIZE_T_STR "unsigned int"
73 #endif
74 #elif defined(__clang__)
75 #define ECS_SIZE_T_STR "size_t"
76 #else
77 #ifdef ECS_TARGET_WINDOWS
78 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long long unsigned int"
79 #else
80 #define ECS_SIZE_T_STR "constexpr size_t; size_t = long unsigned int"
81 #endif
82 #endif
83#else
84 #ifdef ECS_TARGET_MSVC
85 #if _MSC_VER >= 1930
86 #define ECS_SIZE_T_STR "unsigned __int32"
87 #else
88 #define ECS_SIZE_T_STR "unsigned int"
89 #endif
90 #elif defined(__clang__)
91 #define ECS_SIZE_T_STR "size_t"
92 #else
93 #ifdef ECS_TARGET_WINDOWS
94 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
95 #else
96 #define ECS_SIZE_T_STR "constexpr size_t; size_t = unsigned int"
97 #endif
98 #endif
99#endif
100
101template <typename E>
102constexpr size_t enum_type_len() {
103 return ECS_FUNC_TYPE_LEN(, enum_type_len, ECS_FUNC_NAME)
104 - (sizeof(ECS_SIZE_T_STR) - 1u);
105}
106
111#if defined(ECS_TARGET_CLANG)
112#if ECS_CLANG_VERSION < 13
113template <typename E, E C>
114constexpr bool enum_constant_is_valid() {
115 return !(
116 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
117 enum_type_len<E>() + 6 /* ', C = ' */] >= '0') &&
118 (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
119 enum_type_len<E>() + 6 /* ', C = ' */] <= '9'));
120}
121#else
122template <typename E, E C>
123constexpr bool enum_constant_is_valid() {
124 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
125 enum_type_len<E>() + 6 /* ', E C = ' */] != '(');
126}
127#endif
128#elif defined(ECS_TARGET_GNU)
129template <typename E, E C>
130constexpr bool enum_constant_is_valid() {
131 return (ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(constepxr bool, enum_constant_is_valid) +
132 enum_type_len<E>() + 8 /* ', E C = ' */] != '(');
133}
134#else
135/* Use different trick on MSVC, since it uses hexadecimal representation for
136 * invalid enum constants. We can leverage that msvc inserts a C-style cast
137 * into the name, and the location of its first character ('(') is known. */
138template <typename E, E C>
139constexpr bool enum_constant_is_valid() {
140 return ECS_FUNC_NAME[ECS_FUNC_NAME_FRONT(bool, enum_constant_is_valid) +
141 enum_type_len<E>() + 1] != '(';
142}
143#endif
144
145template <typename E, E C>
147 static constexpr bool value = enum_constant_is_valid<E, C>();
148};
149
151template <typename E, E C>
152static const char* enum_constant_to_name() {
153 static const size_t len = ECS_FUNC_TYPE_LEN(const char*, enum_constant_to_name, ECS_FUNC_NAME);
154 static char result[len + 1] = {};
155 return ecs_cpp_get_constant_name(
156 result, ECS_FUNC_NAME, string::length(ECS_FUNC_NAME),
157 ECS_FUNC_NAME_BACK);
158}
159
162 flecs::entity_t id;
163 int next;
164};
165
168 flecs::entity_t id;
169 int min;
170 int max;
171 enum_constant_data constants[FLECS_ENUM_MAX_COUNT];
172};
173
175template <typename E>
176struct enum_type {
177 static enum_data_impl data;
178
179 static enum_type<E>& get() {
180 static _::enum_type<E> instance;
181 return instance;
182 }
183
184 flecs::entity_t entity(E value) const {
185 return data.constants[static_cast<int>(value)].id;
186 }
187
188 void init(flecs::world_t *world, flecs::entity_t id) {
189#if !FLECS_CPP_ENUM_REFLECTION_SUPPORT
190 ecs_abort(ECS_UNSUPPORTED, "enum reflection requires gcc 7.5 or higher")
191#endif
192
193 ecs_log_push();
194 ecs_cpp_enum_init(world, id);
195 data.id = id;
196 data.min = FLECS_ENUM_MAX(int);
197 init< enum_last<E>::value >(world);
198 ecs_log_pop();
199 }
200
201private:
202 template <E Value>
203 static constexpr int to_int() {
204 return static_cast<int>(Value);
205 }
206
207 template <int Value>
208 static constexpr E from_int() {
210 }
211
212 template <E Value>
213 static constexpr int is_not_0() {
214 return static_cast<int>(Value != from_int<0>());
215 }
216
217 template <E Value, flecs::if_not_t< enum_constant_is_valid<E, Value>() > = 0>
218 static void init_constant(flecs::world_t*) { }
219
220 template <E Value, flecs::if_t< enum_constant_is_valid<E, Value>() > = 0>
221 static void init_constant(flecs::world_t *world) {
222 int v = to_int<Value>();
223 const char *name = enum_constant_to_name<E, Value>();
224 data.constants[v].next = data.min;
225 data.min = v;
226 if (!data.max) {
227 data.max = v;
228 }
229
230 data.constants[v].id = ecs_cpp_enum_constant_register(
231 world, data.id, data.constants[v].id, name, v);
232 }
233
234 template <E Value = FLECS_ENUM_MAX(E) >
235 static void init(flecs::world_t *world) {
236 init_constant<Value>(world);
237 if (is_not_0<Value>()) {
238 init<from_int<to_int<Value>() - is_not_0<Value>()>()>(world);
239 }
240 }
241};
242
243template <typename E>
245
246template <typename E, if_t< is_enum<E>::value > = 0>
247inline static void init_enum(flecs::world_t *world, flecs::entity_t id) {
248 _::enum_type<E>::get().init(world, id);
249}
250
251template <typename E, if_not_t< is_enum<E>::value > = 0>
252inline static void init_enum(flecs::world_t*, flecs::entity_t) { }
253
254} // namespace _
255
257template <typename E>
258struct enum_data {
259 enum_data(flecs::world_t *world, _::enum_data_impl& impl)
260 : world_(world)
261 , impl_(impl) { }
262
263 bool is_valid(int value) {
264 return impl_.constants[value].id != 0;
265 }
266
267 int first() const {
268 return impl_.min;
269 }
270
271 int last() const {
272 return impl_.max;
273 }
274
275 int next(int cur) const {
276 return impl_.constants[cur].next;
277 }
278
279 flecs::entity entity() const;
280 flecs::entity entity(int value) const;
281 flecs::entity entity(E value) const;
282
283 flecs::world_t *world_;
284 _::enum_data_impl& impl_;
285};
286
288template <typename E>
289enum_data<E> enum_type(flecs::world_t *world) {
290 _::cpp_type<E>::id(world); // Ensure enum is registered
291 auto& ref = _::enum_type<E>::get();
292 return enum_data<E>(world, ref.data);
293}
294
295} // namespace flecs
#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:139
Enumeration constant data.
Definition enum.hpp:161
Enumeration type data.
Definition enum.hpp:167
Class that scans an enum for constants, extracts names & creates entities.
Definition enum.hpp:176
Entity.
Definition entity.hpp:30
Convenience type with enum reflection data.
Definition enum.hpp:258
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
Component reference.
Definition ref.hpp:23
The world.
Definition world.hpp:132