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
20template <typename T>
21inline const char* component_symbol_name() {
22 return nullptr;
23}
24
25template <> inline const char* component_symbol_name<uint8_t>() {
26 return "u8";
27}
28template <> inline const char* component_symbol_name<uint16_t>() {
29 return "u16";
30}
31template <> inline const char* component_symbol_name<uint32_t>() {
32 return "u32";
33}
34template <> inline const char* component_symbol_name<uint64_t>() {
35 return "u64";
36}
37template <> inline const char* component_symbol_name<int8_t>() {
38 return "i8";
39}
40template <> inline const char* component_symbol_name<int16_t>() {
41 return "i16";
42}
43template <> inline const char* component_symbol_name<int32_t>() {
44 return "i32";
45}
46template <> inline const char* component_symbol_name<int64_t>() {
47 return "i64";
48}
49template <> inline const char* component_symbol_name<float>() {
50 return "f32";
51}
52template <> inline const char* component_symbol_name<double>() {
53 return "f64";
54}
55
56// If the type is trivial, don't register lifecycle actions. While the functions
57// that obtain the lifecycle callback do detect whether the callback is required,
58// adding a special case for trivial types eases the burden a bit on the
59// compiler, as it reduces the number of templates to evaluate.
60template<typename T>
61void register_lifecycle_actions(
64{
65 (void)world; (void)component;
66 if constexpr (!(std::is_trivially_default_constructible<T>::value &&
67 std::is_trivially_copyable<T>::value))
68 {
69 // If the component is non-trivial, register component lifecycle actions.
70 // Depending on the type, not all callbacks may be available.
72 cl.ctor = ctor<T>(cl.flags);
73 cl.dtor = dtor<T>(cl.flags);
74
75 cl.copy = copy<T>(cl.flags);
76 cl.copy_ctor = copy_ctor<T>(cl.flags);
77 cl.move = move<T>(cl.flags);
78 cl.move_ctor = move_ctor<T>(cl.flags);
79
80 cl.ctor_move_dtor = ctor_move_dtor<T>(cl.flags);
81 cl.move_dtor = move_dtor<T>(cl.flags);
82
83 cl.flags &= ECS_TYPE_HOOKS_ILLEGAL;
85
86 if (cl.flags & (ECS_TYPE_HOOK_MOVE_ILLEGAL|ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL))
87 {
88 ecs_add_id(world, component, flecs::Sparse);
89 }
90 }
91}
92
93template <typename T>
94inline ecs_cpp_type_action_t lifecycle_action() {
95 if constexpr (std::is_trivially_default_constructible<T>::value &&
96 std::is_trivially_copyable<T>::value)
97 {
98 return nullptr;
99 } else {
100 return &register_lifecycle_actions<T>;
101 }
102}
103
104#ifdef FLECS_META
105template <typename T, typename = void>
106struct has_cpp_meta_desc : std::false_type {};
107
108template <typename T>
109struct has_cpp_meta_desc<T, decltype(void(
110 flecs_meta_cpp_desc(static_cast<T*>(nullptr))))> : std::true_type {};
111#endif
112
113template <typename T>
114inline ecs_cpp_type_action_t enum_action() {
115#if FLECS_CPP_ENUM_REFLECTION_SUPPORT
116#ifdef FLECS_META
117 if constexpr (has_cpp_meta_desc<T>::value) {
118 return nullptr;
119 } else
120#endif
121 if constexpr (is_enum_v<T>) {
122 return &_::init_enum<T>;
123 } else {
124 return nullptr;
125 }
126#else
127 return nullptr;
128#endif
129}
130
131#ifdef FLECS_META
132
133template <typename T>
134inline void register_cpp_meta(ecs_world_t *world, ecs_entity_t component) {
135 (void)world; (void)component;
136 if constexpr (has_cpp_meta_desc<T>::value) {
137 ecs_type_kind_t kind = flecs_meta_cpp_kind(static_cast<T*>(nullptr));
138 if (kind == EcsStructType && ecs_has_id(world, component,
139 ecs_id(EcsStruct))) {
140 return;
141 }
142 if (kind == EcsEnumType && ecs_has_id(world, component,
143 ecs_id(EcsEnum))) {
144 return;
145 }
146 if (kind == EcsBitmaskType && ecs_has_id(world, component,
147 ecs_id(EcsBitmask))) {
148 return;
149 }
150 ecs_meta_from_desc(world, component, kind,
151 flecs_meta_cpp_desc(static_cast<T*>(nullptr)));
152 }
153}
154#endif
155
156template <typename T>
157struct type_impl {
158 static_assert(is_pointer<T>::value == false,
159 "pointer types are not allowed for components");
160
161 // Initialize the component identifier.
162 static void init(
163 bool allow_tag = true)
164 {
165#ifdef FLECS_MULTI_WORLD
166 index(); // Make sure the global component index is initialized.
167#endif
168
169 s_size = sizeof(T);
170 s_alignment = alignof(T);
171 if (is_empty<T>::value && allow_tag) {
172 s_size = 0;
173 s_alignment = 0;
174 }
175 }
176
177 static void init_builtin(
180 bool allow_tag = true)
181 {
182 init(allow_tag);
183#ifdef FLECS_MULTI_WORLD
184 flecs_component_ids_set(world, index(), id);
185#else
186 (void)world;
187 s_id = id;
188#endif
189 }
190
191 // Register the component ID.
192 static entity_t register_id(
193 world_t *world, // The world
194 const char *name = nullptr, // User-provided name (overrides typename)
195 bool allow_tag = true, // Register empty types as zero-sized components
196 bool is_component = true, // Add flecs::Component to the result
197 bool explicit_registration = false, // Entered from world.component<T>()?
198 flecs::id_t id = 0) // User-provided component ID
199 {
200 init(allow_tag);
201#ifdef FLECS_MULTI_WORLD
202 ecs_assert(index() != 0, ECS_INTERNAL_ERROR, nullptr);
203#endif
204
206 id,
207#ifdef FLECS_MULTI_WORLD
208 index(),
209#else
210 &s_id,
211#endif
212 name,
213 type_name<T>(),
214 component_symbol_name<T>(),
215 size(),
216 alignment(),
217 lifecycle_action<T>(),
218 enum_action<T>(),
219 is_component,
220 explicit_registration
221 };
222
224
225 ecs_assert(c != 0, ECS_INTERNAL_ERROR, nullptr);
226
227 if constexpr (flecs::sparse<T>::value) {
228 ecs_add_id(world, c, flecs::Sparse);
229 }
230
231 if constexpr (flecs::dont_fragment<T>::value) {
232 ecs_add_id(world, c, flecs::DontFragment);
233 }
234
236 constexpr flecs::on_instantiate policy =
238#ifdef FLECS_PREFAB
239 if constexpr (policy == flecs::on_instantiate::override) {
240 ecs_add_pair(world, c, flecs::OnInstantiate, flecs::Override);
241 }
242 if constexpr (policy == flecs::on_instantiate::inherit) {
243 ecs_add_pair(world, c, flecs::OnInstantiate, flecs::Inherit);
244 }
245#endif
246 if constexpr (policy == flecs::on_instantiate::dont_inherit) {
247 ecs_add_pair(world, c, flecs::OnInstantiate,
248 flecs::DontInherit);
249 }
250 }
251
252#ifdef FLECS_META
253 register_cpp_meta<T>(world, c);
254#endif
255
256 return c;
257 }
258
259 // Get the type (component) ID.
260 // If the type was not yet registered and automatic registration is allowed,
261 // this function will also register the type.
262 static entity_t id(world_t *world)
263 {
264#ifdef FLECS_CPP_NO_AUTO_REGISTRATION
266 "component '%s' must be registered before use",
267 type_name<T>());
268
269#ifdef FLECS_MULTI_WORLD
270 flecs::entity_t c = flecs_component_ids_get(world, index());
271#else
272 flecs::entity_t c = s_id;
273#endif
274 ecs_assert(c != 0, ECS_INTERNAL_ERROR, nullptr);
276 "component '%s' was deleted, reregister before using",
277 type_name<T>());
278#else
279#ifdef FLECS_MULTI_WORLD
280 flecs::entity_t c = flecs_component_ids_get_alive(world, index());
281#else
282 flecs::entity_t c = s_id;
283 if (c && !ecs_is_alive(world, c)) {
284 c = 0;
285 }
286#endif
287 if (!c) {
288 c = register_id(world);
289 }
290#endif
291 return c;
292 }
293
294 // Return the size of a component.
295 static size_t size() {
296 return s_size;
297 }
298
299 // Return the alignment of a component.
300 static size_t alignment() {
301 return s_alignment;
302 }
303
304 // Was the component already registered?
305 static bool registered(flecs::world_t *world) {
306 ecs_assert(world != nullptr, ECS_INVALID_PARAMETER, nullptr);
307
308#ifdef FLECS_MULTI_WORLD
309 if (!flecs_component_ids_get(world, index())) {
310#else
311 if (!s_id) {
312#endif
313 return false;
314 }
315
316 return true;
317 }
318
319 // This function is only used to test cross-translation-unit features. No
320 // code other than test cases should invoke this function.
321 static void reset() {
322 s_size = 0;
323 s_alignment = 0;
324#ifndef FLECS_MULTI_WORLD
325 s_id = 0;
326#endif
327 }
328
329#ifdef FLECS_MULTI_WORLD
330 static int32_t index() {
331 static int32_t index_ = flecs_component_ids_index_get();
332 return index_;
333 }
334#else
335 static entity_t s_id;
336#endif
337
338 static size_t s_size;
339 static size_t s_alignment;
340};
341
342// Global templated variables that hold the component identifier and other info.
343template <typename T> inline size_t type_impl<T>::s_size;
344template <typename T> inline size_t type_impl<T>::s_alignment;
345#ifndef FLECS_MULTI_WORLD
346template <typename T> inline entity_t type_impl<T>::s_id;
347#endif
348
349// Front-facing class for implicitly registering a component and obtaining
350// static component data.
351
352// Regular type.
353template <typename T>
354struct type<T, if_not_t< is_pair<T>::value >>
355 : type_impl<base_type_t<T>> { };
356
357// Pair type.
358template <typename T>
359struct type<T, if_t< is_pair<T>::value >>
360{
361 // Override the id() method to return the ID of a pair.
362 static id_t id(world_t *world = nullptr) {
363 return ecs_pair(
364 type< pair_first_t<T> >::id(world),
365 type< pair_second_t<T> >::id(world));
366 }
367};
368
369} // namespace _
370
377 using entity::entity;
378
381
388
394
400 explicit untyped_component(flecs::world_t *world, const char *name)
401 {
402 world_ = world;
403
404 ecs_entity_desc_t desc = {};
405 desc.name = name;
406 desc.sep = "::";
407 desc.root_sep = "::";
408 desc.use_low_id = true;
409 id_ = ecs_entity_init(world, &desc);
410 }
411
419 explicit untyped_component(world_t *world, const char *name, const char *sep, const char *root_sep)
420 {
421 world_ = world;
422
423 ecs_entity_desc_t desc = {};
424 desc.name = name;
425 desc.sep = sep;
426 desc.root_sep = root_sep;
427 desc.use_low_id = true;
428 id_ = ecs_entity_init(world, &desc);
429 }
430
431protected:
432
438 const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_);
439 if (h) {
440 return *h;
441 } else {
442 return {};
443 }
444 }
445
451 h.flags &= ECS_TYPE_HOOKS_ILLEGAL;
452 ecs_set_hooks_id(world_, id_, &h);
453 }
454
455public:
456
463 ecs_cmp_t compare_callback)
464{
465 ecs_assert(compare_callback, ECS_INVALID_PARAMETER, nullptr);
466 flecs::type_hooks_t h = get_hooks();
467 h.cmp = compare_callback;
468 h.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL;
469 if(h.flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) {
470 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
471 h.equals = nullptr;
472 }
473 set_hooks(h);
474 return *this;
475}
476
483 ecs_equals_t equals_callback)
484{
485 ecs_assert(equals_callback, ECS_INVALID_PARAMETER, nullptr);
486 flecs::type_hooks_t h = get_hooks();
487 h.equals = equals_callback;
488 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
489 set_hooks(h);
490 return *this;
491}
492
493# ifdef FLECS_META
495# endif
496# ifdef FLECS_METRICS
498# endif
499};
500
506template <typename T>
519 const char *name = nullptr,
520 bool allow_tag = true,
521 flecs::id_t id = 0)
522 {
523 world_ = world;
524 id_ = _::type<T>::register_id(world, name, allow_tag, true, true, id);
525 }
526
532 template <typename Func>
533 component<T>& on_add(Func&& func) {
534 using Delegate = typename _::each_delegate<typename std::decay<Func>::type, T>;
535 flecs::type_hooks_t h = get_hooks();
537 "on_add hook is already set");
538 BindingCtx *ctx = get_binding_ctx(h);
539 h.on_add = Delegate::run_add;
540 ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func));
541 ctx->free_on_add = _::free_obj<Delegate>;
542 set_hooks(h);
543 return *this;
544 }
545
551 template <typename Func>
552 component<T>& on_remove(Func&& func) {
553 using Delegate = typename _::each_delegate<
554 typename std::decay<Func>::type, T>;
555 flecs::type_hooks_t h = get_hooks();
557 "on_remove hook is already set");
558 BindingCtx *ctx = get_binding_ctx(h);
559 h.on_remove = Delegate::run_remove;
560 ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func));
561 ctx->free_on_remove = _::free_obj<Delegate>;
562 set_hooks(h);
563 return *this;
564 }
565
571 template <typename Func>
572 component<T>& on_set(Func&& func) {
573 using Delegate = typename _::each_delegate<
574 typename std::decay<Func>::type, T>;
575 flecs::type_hooks_t h = get_hooks();
577 "on_set hook is already set");
578 BindingCtx *ctx = get_binding_ctx(h);
579 h.on_set = Delegate::run_set;
580 ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func));
581 ctx->free_on_set = _::free_obj<Delegate>;
582 set_hooks(h);
583 return *this;
584 }
585
591 template <typename Func>
592 component<T>& on_replace(Func&& func) {
593 using Delegate = typename _::each_delegate<
594 typename std::decay<Func>::type, T, T>;
595 flecs::type_hooks_t h = get_hooks();
597 "on_replace hook is already set");
598 BindingCtx *ctx = get_binding_ctx(h);
599 h.on_replace = Delegate::run_replace;
600 ctx->on_replace = FLECS_NEW(Delegate)(FLECS_FWD(func));
601 ctx->free_on_replace = _::free_obj<Delegate>;
602 set_hooks(h);
603 return *this;
604 }
605
615 template <typename Func>
616 component<T>& on_validate(Func&& func) {
617 using Delegate = _::validate_delegate<
618 typename std::decay<Func>::type, T>;
619 flecs::type_hooks_t h = get_hooks();
621 "on_validate hook is already set");
622 BindingCtx *ctx = get_binding_ctx(h);
623 h.on_validate = Delegate::run;
624 ctx->on_validate = FLECS_NEW(Delegate)(FLECS_FWD(func));
625 ctx->free_on_validate = _::free_obj<Delegate>;
626 set_hooks(h);
627 return *this;
628 }
629
630 using untyped_component::on_compare;
631
637 ecs_cmp_t handler = _::compare<T>();
638 ecs_assert(handler != nullptr, ECS_INVALID_OPERATION,
639 "Type does not have operator> or operator< const or is inaccessible");
640 on_compare(handler);
641 return *this;
642 }
643
644 using cmp_hook = int(*)(const T* a, const T* b, const ecs_type_info_t *ti);
645
651 component<T>& on_compare(cmp_hook callback) {
652 on_compare(reinterpret_cast<ecs_cmp_t>(callback));
653 return *this;
654 }
655
656 using untyped_component::on_equals;
657
663 ecs_equals_t handler = _::equals<T>();
664 ecs_assert(handler != nullptr, ECS_INVALID_OPERATION,
665 "Type does not have operator== const or is inaccessible");
666 on_equals(handler);
667 return *this;
668 }
669
670 using equals_hook = bool(*)(const T* a, const T* b, const ecs_type_info_t *ti);
671
677 component<T>& on_equals(equals_hook callback) {
678 on_equals(reinterpret_cast<ecs_equals_t>(callback));
679 return *this;
680 }
681
682# ifdef FLECS_META
684# endif
685
686private:
687 using BindingCtx = _::component_binding_ctx;
688
689 BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){
690 BindingCtx *result = static_cast<BindingCtx*>(h.binding_ctx);
691 if (!result) {
692 result = FLECS_NEW(BindingCtx);
693 h.binding_ctx = result;
694 h.binding_ctx_free = _::free_obj<BindingCtx>;
695 }
696 return result;
697 }
698};
699
700}
701
Meta component mixin.
FLECS_API ecs_entity_t ecs_cpp_component_register(ecs_world_t *world, const ecs_cpp_component_desc_t *desc)
Register a C++ component.
void ecs_add_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t component)
Add a (component) ID to an entity.
FLECS_API const ecs_entity_t ecs_id(EcsDocDescription)
Component ID for EcsDocDescription.
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
#define ECS_INVALID_OPERATION
Invalid operation error code.
Definition log.h:669
#define ECS_INVALID_PARAMETER
Invalid parameter error code.
Definition log.h:671
#define ECS_INTERNAL_ERROR
Internal error code.
Definition log.h:681
FLECS_API int ecs_meta_from_desc(ecs_world_t *world, ecs_entity_t component, ecs_type_kind_t kind, const char *desc)
Populate meta information from type descriptor.
ecs_type_kind_t
Type kinds supported by meta addon.
Definition meta.h:153
const ecs_type_hooks_t * ecs_get_hooks_id(const ecs_world_t *world, ecs_entity_t component)
Get hooks for a component.
void ecs_set_hooks_id(ecs_world_t *world, ecs_entity_t component, const ecs_type_hooks_t *hooks)
Register hooks for a component.
ecs_id_t ecs_entity_t
An entity identifier.
Definition flecs.h:393
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:437
ecs_id_t id_t
ID type.
Definition c_types.hpp:20
ecs_entity_t entity_t
Entity type.
Definition c_types.hpp:21
ecs_world_t world_t
World type.
Definition c_types.hpp:18
transcribe_cv_t< remove_reference_t< P >, typename raw_type_t< P >::second > pair_second_t
Get pair::second from a pair while preserving cv qualifiers.
Definition pair.hpp:112
transcribe_cv_t< remove_reference_t< P >, typename raw_type_t< P >::first > pair_first_t
Get pair::first from a pair while preserving cv qualifiers.
Definition pair.hpp:108
ecs_entity_t ecs_entity_init(ecs_world_t *world, const ecs_entity_desc_t *desc)
Find or create an entity.
bool ecs_has_id(const ecs_world_t *world, ecs_entity_t entity, ecs_id_t component)
Test if an entity has a component.
#define ecs_add_pair(world, subject, first, second)
Add a pair to an entity.
Definition flecs_c.h:275
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:691
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:697
bool ecs_is_alive(const ecs_world_t *world, ecs_entity_t e)
Test whether an entity is alive.
Meta component mixin.
Metrics component mixin.
Component added to bitmask type entities.
Definition meta.h:309
Component added to enum type entities.
Definition meta.h:289
Component added to struct type entities.
Definition meta.h:268
Used with ecs_entity_init().
Definition flecs.h:1075
const char * sep
Optional custom separator for hierarchical names.
Definition flecs.h:1087
const char * root_sep
Optional, used for identifiers relative to the root.
Definition flecs.h:1091
const char * name
Name of the entity.
Definition flecs.h:1082
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:1103
ecs_iter_action_t on_remove
Callback that is invoked when an instance of the component is removed.
Definition flecs.h:1024
void * binding_ctx
Language binding context.
Definition flecs.h:1038
ecs_flags32_t flags
Hook flags.
Definition flecs.h:1010
ecs_cmp_t cmp
Compare hook.
Definition flecs.h:1001
ecs_on_validate_t on_validate
Callback that is invoked before the on_set/OnSet hooks and observers are invoked.
Definition flecs.h:1035
ecs_iter_action_t on_set
Callback that is invoked when an instance of the component is set.
Definition flecs.h:1019
ecs_xtor_t ctor
ctor.
Definition flecs.h:977
ecs_iter_action_t on_replace
Callback that is invoked with the existing and new value before the value is assigned.
Definition flecs.h:1030
ecs_iter_action_t on_add
Callback that is invoked when an instance of a component is added.
Definition flecs.h:1014
ecs_ctx_free_t binding_ctx_free
Callback to free binding_ctx.
Definition flecs.h:1042
ecs_equals_t equals
Equals hook.
Definition flecs.h:1004
Type that contains component information (passed to ctors/dtors/...).
Definition flecs.h:1050
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.
component< T > & on_compare(cmp_hook callback)
Type-safe variant of the compare op function.
component< T > & on_add(Func &&func)
Register on_add hook.
component< T > & on_equals()
Register an operator equals hook using type T's equality operator.
component< T > & on_set(Func &&func)
Register on_set hook.
component< T > & on_validate(Func &&func)
Register on_validate hook.
component< T > & on_equals(equals_hook callback)
Type-safe variant of the equals op function.
component< T > & on_compare()
Register an operator compare hook using type T's comparison operators.
Trait that marks a component as DontFragment at compile time.
Definition utils.hpp:178
Entity.
Definition entity.hpp:30
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
Test if a type is a pair.
Definition pair.hpp:98
Trait that assigns an OnInstantiate policy to a component at compile time.
Definition utils.hpp:204
Trait that marks a component as Sparse at compile time.
Definition utils.hpp:187
Untyped component class.
untyped_component(flecs::world_t *world, flecs::entity_t id)
Construct from world and entity ID.
untyped_component(world_t *world, const char *name, const char *sep, const char *root_sep)
Construct from world, name, and scope separators.
untyped_component & on_compare(ecs_cmp_t compare_callback)
Register a custom compare hook for this component.
flecs::type_hooks_t get_hooks() const
Get the type hooks for this component.
untyped_component()
Default constructor.
untyped_component(flecs::entity_t id)
Construct from entity ID.
void set_hooks(flecs::type_hooks_t &h)
Set the type hooks for this component.
untyped_component(flecs::world_t *world, const char *name)
Construct from world and name.
untyped_component & on_equals(ecs_equals_t equals_callback)
Register a custom equals hook for this component.
The world.
Definition world.hpp:244
enable_if_t< false==V, int > if_not_t
Convenience enable_if alias for negated conditions.
Definition utils.hpp:172
enable_if_t< V, int > if_t
Convenience enable_if alias using int as default type.
Definition utils.hpp:168
on_instantiate
OnInstantiate policies that can be assigned to a component at compile time with the flecs::on_instant...
Definition utils.hpp:194