Flecs v4.1
A fast entity component system (ECS) for C & C++
Loading...
Searching...
No Matches
component.hpp
Go to the documentation of this file.
1
6#pragma once
7
16namespace flecs {
17
18namespace _ {
19
20// Translate a typename into a language-agnostic identifier. This allows for
21// registration of components/modules across language boundaries.
22template <typename T>
23inline const char* symbol_name() {
24 static const size_t len = ECS_FUNC_TYPE_LEN(const char*, symbol_name, ECS_FUNC_NAME);
25 static char result[len + 1] = {};
26 static const char* cppSymbolName = ecs_cpp_get_symbol_name(result, type_name<T>(), len);
27 return cppSymbolName;
28}
29
30template <> inline const char* symbol_name<uint8_t>() {
31 return "u8";
32}
33template <> inline const char* symbol_name<uint16_t>() {
34 return "u16";
35}
36template <> inline const char* symbol_name<uint32_t>() {
37 return "u32";
38}
39template <> inline const char* symbol_name<uint64_t>() {
40 return "u64";
41}
42template <> inline const char* symbol_name<int8_t>() {
43 return "i8";
44}
45template <> inline const char* symbol_name<int16_t>() {
46 return "i16";
47}
48template <> inline const char* symbol_name<int32_t>() {
49 return "i32";
50}
51template <> inline const char* symbol_name<int64_t>() {
52 return "i64";
53}
54template <> inline const char* symbol_name<float>() {
55 return "f32";
56}
57template <> inline const char* symbol_name<double>() {
58 return "f64";
59}
60
61// If type is trivial, don't register lifecycle actions. While the functions
62// that obtain the lifecycle callback do detect whether the callback is required
63// adding a special case for trivial types eases the burden a bit on the
64// compiler as it reduces the number of templates to evaluate.
65template<typename T>
66void register_lifecycle_actions(
69{
70 (void)world; (void)component;
71 if constexpr (!std::is_trivial<T>::value) {
72 // If the component is non-trivial, register component lifecycle actions.
73 // Depending on the type not all callbacks may be available.
75 cl.ctor = ctor<T>(cl.flags);
76 cl.dtor = dtor<T>(cl.flags);
77
78 cl.copy = copy<T>(cl.flags);
79 cl.copy_ctor = copy_ctor<T>(cl.flags);
80 cl.move = move<T>(cl.flags);
81 cl.move_ctor = move_ctor<T>(cl.flags);
82
83 cl.ctor_move_dtor = ctor_move_dtor<T>(cl.flags);
84 cl.move_dtor = move_dtor<T>(cl.flags);
85
86 cl.flags &= ECS_TYPE_HOOKS_ILLEGAL;
88
89 if (cl.flags & (ECS_TYPE_HOOK_MOVE_ILLEGAL|ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL))
90 {
91 ecs_add_id(world, component, flecs::Sparse);
92 }
93 }
94}
95
96template <typename T>
97struct type_impl {
98 static_assert(is_pointer<T>::value == false,
99 "pointer types are not allowed for components");
100
101 // Initialize component identifier
102 static void init(
103 bool allow_tag = true)
104 {
105 s_index = flecs_component_ids_index_get();
106 s_allow_tag = allow_tag;
107 s_size = sizeof(T);
108 s_alignment = alignof(T);
109 if (is_empty<T>::value && allow_tag) {
110 s_size = 0;
111 s_alignment = 0;
112 }
113 }
114
115 static void init_builtin(
116 flecs::world_t *world,
117 flecs::entity_t id,
118 bool allow_tag = true)
119 {
120 init(allow_tag);
121 flecs_component_ids_set(world, s_index, id);
122 }
123
124 // Register component id.
125 static entity_t register_id(
126 world_t *world, // The world
127 const char *name = nullptr, // User provided name (overrides typename)
128 bool allow_tag = true, // Register empty types as zero-sized components
129 bool is_component = true, // Add flecs::Component to result
130 bool explicit_registration = false, // Entered from world.component<T>()?
131 flecs::id_t id = 0) // User provided component id
132 {
133 if (!s_index) {
134 // This is the first time (in this binary image) that this type is
135 // being used. Generate a static index that will identify the type
136 // across worlds.
137 init(allow_tag);
138 ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL);
139 }
140
141 bool registered = false, existing = false;
142
143 flecs::entity_t c = ecs_cpp_component_register(
144 world, id, s_index, name, type_name<T>(),
145 symbol_name<T>(), size(), alignment(),
146 is_component, explicit_registration, &registered, &existing);
147
148 ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL);
149
150 if (registered) {
151 // Register lifecycle callbacks, but only if the component has a
152 // size. Components that don't have a size are tags, and tags don't
153 // require construction/destruction/copy/move's.
154 if (size() && !existing) {
155 register_lifecycle_actions<T>(world, c);
156 }
157
158 // If component is enum type, register constants. Make sure to do
159 // this after setting the component id, because the enum code will
160 // be calling type<T>::id().
161 #if FLECS_CPP_ENUM_REFLECTION_SUPPORT
162 _::init_enum<T>(world, c);
163 #endif
164 }
165
166 return c;
167 }
168
169 // Get type (component) id.
170 // If type was not yet registered and automatic registration is allowed,
171 // this function will also register the type.
172 static entity_t id(world_t *world)
173 {
174#ifdef FLECS_CPP_NO_AUTO_REGISTRATION
175 ecs_assert(registered(world), ECS_INVALID_OPERATION,
176 "component '%s' must be registered before use",
177 type_name<T>());
178
179 flecs::entity_t c = flecs_component_ids_get(world, s_index);
180 ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL);
181 ecs_assert(ecs_is_alive(world, c), ECS_INVALID_OPERATION,
182 "component '%s' was deleted, reregister before using",
183 type_name<T>());
184#else
185 flecs::entity_t c = flecs_component_ids_get_alive(world, s_index);
186 if (!c) {
187 c = register_id(world);
188 }
189#endif
190 return c;
191 }
192
193 // Return the size of a component.
194 static size_t size() {
195 ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL);
196 return s_size;
197 }
198
199 // Return the alignment of a component.
200 static size_t alignment() {
201 ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL);
202 return s_alignment;
203 }
204
205 // Was the component already registered.
206 static bool registered(flecs::world_t *world) {
207 ecs_assert(world != nullptr, ECS_INVALID_PARAMETER, NULL);
208
209 if (s_index == 0) {
210 return false;
211 }
212
213 if (!flecs_component_ids_get(world, s_index)) {
214 return false;
215 }
216
217 return true;
218 }
219
220 // This function is only used to test cross-translation unit features. No
221 // code other than test cases should invoke this function.
222 static void reset() {
223 s_index = 0;
224 s_size = 0;
225 s_alignment = 0;
226 s_allow_tag = true;
227 }
228
229 static int32_t s_index;
230 static size_t s_size;
231 static size_t s_alignment;
232 static bool s_allow_tag;
233};
234
235// Global templated variables that hold component identifier and other info
236template <typename T> inline int32_t type_impl<T>::s_index;
237template <typename T> inline size_t type_impl<T>::s_size;
238template <typename T> inline size_t type_impl<T>::s_alignment;
239template <typename T> inline bool type_impl<T>::s_allow_tag( true );
240
241// Front facing class for implicitly registering a component & obtaining
242// static component data
243
244// Regular type
245template <typename T>
246struct type<T, if_not_t< is_pair<T>::value >>
247 : type_impl<base_type_t<T>> { };
248
249// Pair type
250template <typename T>
251struct type<T, if_t< is_pair<T>::value >>
252{
253 // Override id method to return id of pair
254 static id_t id(world_t *world = nullptr) {
255 return ecs_pair(
256 type< pair_first_t<T> >::id(world),
257 type< pair_second_t<T> >::id(world));
258 }
259};
260
261} // namespace _
262
269 using entity::entity;
270
271 untyped_component() : entity() { }
272 explicit untyped_component(flecs::world_t *world, flecs::entity_t id) : entity(world, id) { }
273 explicit untyped_component(flecs::entity_t id) : entity(id) { }
274
275 explicit untyped_component(flecs::world_t *world, const char *name)
276 {
277 world_ = world;
278
279 ecs_entity_desc_t desc = {};
280 desc.name = name;
281 desc.sep = "::";
282 desc.root_sep = "::";
283 desc.use_low_id = true;
284 id_ = ecs_entity_init(world, &desc);
285 }
286
287 explicit untyped_component(world_t *world, const char *name, const char *sep, const char *root_sep)
288 {
289 world_ = world;
290
291 ecs_entity_desc_t desc = {};
292 desc.name = name;
293 desc.sep = sep;
294 desc.root_sep = root_sep;
295 desc.use_low_id = true;
296 id_ = ecs_entity_init(world, &desc);
297 }
298
299protected:
300
301flecs::type_hooks_t get_hooks() const {
302 const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_);
303 if (h) {
304 return *h;
305 } else {
306 return {};
307 }
308}
309
310void set_hooks(flecs::type_hooks_t &h) {
311 h.flags &= ECS_TYPE_HOOKS_ILLEGAL;
312 ecs_set_hooks_id(world_, id_, &h);
313}
314
315public:
316
318 ecs_cmp_t compare_callback)
319{
320 ecs_assert(compare_callback, ECS_INVALID_PARAMETER, NULL);
321 flecs::type_hooks_t h = get_hooks();
322 h.cmp = compare_callback;
323 h.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL;
324 if(h.flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) {
325 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
326 h.equals = NULL;
327 }
328 set_hooks(h);
329 return *this;
330}
331
333 ecs_equals_t equals_callback)
334{
335 ecs_assert(equals_callback, ECS_INVALID_PARAMETER, NULL);
336 flecs::type_hooks_t h = get_hooks();
337 h.equals = equals_callback;
338 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
339 set_hooks(h);
340 return *this;
341}
342
343# ifdef FLECS_META
345# endif
346# ifdef FLECS_METRICS
347# include "mixins/metrics/untyped_component.inl"
348# endif
349};
350
356template <typename T>
368 flecs::world_t *world,
369 const char *name = nullptr,
370 bool allow_tag = true,
371 flecs::id_t id = 0)
372 {
373 world_ = world;
374 id_ = _::type<T>::register_id(world, name, allow_tag, true, true, id);
375 }
376
378 template <typename Func>
379 component<T>& on_add(Func&& func) {
380 using Delegate = typename _::each_delegate<typename std::decay<Func>::type, T>;
381 flecs::type_hooks_t h = get_hooks();
382 ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION,
383 "on_add hook is already set");
384 BindingCtx *ctx = get_binding_ctx(h);
385 h.on_add = Delegate::run_add;
386 ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func));
387 ctx->free_on_add = _::free_obj<Delegate>;
388 set_hooks(h);
389 return *this;
390 }
391
393 template <typename Func>
394 component<T>& on_remove(Func&& func) {
395 using Delegate = typename _::each_delegate<
396 typename std::decay<Func>::type, T>;
397 flecs::type_hooks_t h = get_hooks();
398 ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION,
399 "on_remove hook is already set");
400 BindingCtx *ctx = get_binding_ctx(h);
401 h.on_remove = Delegate::run_remove;
402 ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func));
403 ctx->free_on_remove = _::free_obj<Delegate>;
404 set_hooks(h);
405 return *this;
406 }
407
409 template <typename Func>
410 component<T>& on_set(Func&& func) {
411 using Delegate = typename _::each_delegate<
412 typename std::decay<Func>::type, T>;
413 flecs::type_hooks_t h = get_hooks();
414 ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION,
415 "on_set hook is already set");
416 BindingCtx *ctx = get_binding_ctx(h);
417 h.on_set = Delegate::run_set;
418 ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func));
419 ctx->free_on_set = _::free_obj<Delegate>;
420 set_hooks(h);
421 return *this;
422 }
423
425 template <typename Func>
426 component<T>& on_replace(Func&& func) {
427 using Delegate = typename _::each_delegate<
428 typename std::decay<Func>::type, T, T>;
429 flecs::type_hooks_t h = get_hooks();
430 ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION,
431 "on_set hook is already set");
432 BindingCtx *ctx = get_binding_ctx(h);
433 h.on_replace = Delegate::run_replace;
434 ctx->on_replace = FLECS_NEW(Delegate)(FLECS_FWD(func));
435 ctx->free_on_replace = _::free_obj<Delegate>;
436 set_hooks(h);
437 return *this;
438 }
439
441 using untyped_component::on_compare;
442 component<T>& on_compare() {
443 ecs_cmp_t handler = _::compare<T>();
444 ecs_assert(handler != NULL, ECS_INVALID_OPERATION,
445 "Type does not have operator> or operator< const or is inaccessible");
446 on_compare(handler);
447 return *this;
448 }
449
451 using cmp_hook = int(*)(const T* a, const T* b, const ecs_type_info_t *ti);
452 component<T>& on_compare(cmp_hook callback) {
453 on_compare(reinterpret_cast<ecs_cmp_t>(callback));
454 return *this;
455 }
456
458 using untyped_component::on_equals;
459 component<T>& on_equals() {
460 ecs_equals_t handler = _::equals<T>();
461 ecs_assert(handler != NULL, ECS_INVALID_OPERATION,
462 "Type does not have operator== const or is inaccessible");
463 on_equals(handler);
464 return *this;
465 }
466
468 using equals_hook = bool(*)(const T* a, const T* b, const ecs_type_info_t *ti);
469 component<T>& on_equals(equals_hook callback) {
470 on_equals(reinterpret_cast<ecs_equals_t>(callback));
471 return *this;
472 }
473
474# ifdef FLECS_META
475# include "mixins/meta/component.inl"
476# endif
477
478private:
479 using BindingCtx = _::component_binding_ctx;
480
481 BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){
482 BindingCtx *result = static_cast<BindingCtx*>(h.binding_ctx);
483 if (!result) {
484 result = FLECS_NEW(BindingCtx);
485 h.binding_ctx = result;
486 h.binding_ctx_free = _::free_obj<BindingCtx>;
487 }
488 return result;
489 }
490};
491
492}
493
void ecs_add_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t component)
Add a (component) id to an entity.
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:368
const ecs_type_hooks_t * ecs_get_hooks_id(const ecs_world_t *world, ecs_entity_t component)
Get hooks for component.
void ecs_set_hooks_id(ecs_world_t *world, ecs_entity_t component, const ecs_type_hooks_t *hooks)
Register hooks for component.
ecs_id_t ecs_entity_t
An entity identifier.
Definition flecs.h:381
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:425
transcribe_cv_t< remove_reference_t< P >, typename raw_type_t< P >::second > pair_second_t
Get pair::second from pair while preserving cv qualifiers.
Definition pair.hpp:95
transcribe_cv_t< remove_reference_t< P >, typename raw_type_t< P >::first > pair_first_t
Get pair::first from pair while preserving cv qualifiers.
Definition pair.hpp:91
ecs_entity_t ecs_entity_init(ecs_world_t *world, const ecs_entity_desc_t *desc)
Find or create an entity.
int(* ecs_cmp_t)(const void *a_ptr, const void *b_ptr, const ecs_type_info_t *type_info)
Compare hook to compare component instances.
Definition flecs.h:678
bool(* ecs_equals_t)(const void *a_ptr, const void *b_ptr, const ecs_type_info_t *type_info)
Equals operator hook.
Definition flecs.h:684
bool ecs_is_alive(const ecs_world_t *world, ecs_entity_t e)
Test whether an entity is alive.
Meta component mixin.
Used with ecs_entity_init().
Definition flecs.h:1038
const char * sep
Optional custom separator for hierarchical names.
Definition flecs.h:1050
const char * root_sep
Optional, used for identifiers relative to root.
Definition flecs.h:1054
const char * name
Name of the entity.
Definition flecs.h:1045
bool use_low_id
When set to true, a low id (typically reserved for components) will be used to create the entity,...
Definition flecs.h:1066
ecs_iter_action_t on_remove
Callback that is invoked when an instance of the component is removed.
Definition flecs.h:994
void * binding_ctx
Language binding context.
Definition flecs.h:1003
ecs_flags32_t flags
Hook flags.
Definition flecs.h:980
ecs_cmp_t cmp
Compare hook.
Definition flecs.h:971
ecs_iter_action_t on_set
Callback that is invoked when an instance of the component is set.
Definition flecs.h:989
ecs_xtor_t ctor
ctor
Definition flecs.h:947
ecs_iter_action_t on_replace
Callback that is invoked with the existing and new value before the value is assigned.
Definition flecs.h:1000
ecs_iter_action_t on_add
Callback that is invoked when an instance of a component is added.
Definition flecs.h:984
ecs_ctx_free_t binding_ctx_free
Callback to free binding_ctx.
Definition flecs.h:1007
ecs_equals_t equals
Equals hook.
Definition flecs.h:974
Type that contains component information (passed to ctors/dtors/...)
Definition flecs.h:1015
Component class.
component< T > & on_remove(Func &&func)
Register on_remove hook.
component(flecs::world_t *world, const char *name=nullptr, bool allow_tag=true, flecs::id_t id=0)
Register a component.
component< T > & on_replace(Func &&func)
Register on_replace hook.
bool(*)(const T *a, const T *b, const ecs_type_info_t *ti) equals_hook
Type safe variant of equals op function.
component< T > & on_add(Func &&func)
Register on_add hook.
component< T > & on_set(Func &&func)
Register on_set hook.
int(*)(const T *a, const T *b, const ecs_type_info_t *ti) cmp_hook
Type safe variant of compare op function.
flecs::string_view name() const
Return the entity name.
Entity.
Definition entity.hpp:30
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
Test if type is a pair.
Definition pair.hpp:82
Untyped component class.
The world.
Definition world.hpp:174