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_trivial<T>::value) {
67 // If the component is non-trivial, register component lifecycle actions.
68 // Depending on the type, not all callbacks may be available.
70 cl.ctor = ctor<T>(cl.flags);
71 cl.dtor = dtor<T>(cl.flags);
72
73 cl.copy = copy<T>(cl.flags);
74 cl.copy_ctor = copy_ctor<T>(cl.flags);
75 cl.move = move<T>(cl.flags);
76 cl.move_ctor = move_ctor<T>(cl.flags);
77
78 cl.ctor_move_dtor = ctor_move_dtor<T>(cl.flags);
79 cl.move_dtor = move_dtor<T>(cl.flags);
80
81 cl.flags &= ECS_TYPE_HOOKS_ILLEGAL;
83
84 if (cl.flags & (ECS_TYPE_HOOK_MOVE_ILLEGAL|ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL))
85 {
86 ecs_add_id(world, component, flecs::Sparse);
87 }
88 }
89}
90
91template <typename T>
92inline ecs_cpp_type_action_t lifecycle_action() {
93 if constexpr (std::is_trivial<T>::value) {
94 return nullptr;
95 } else {
96 return &register_lifecycle_actions<T>;
97 }
98}
99
100#ifdef FLECS_META
101template <typename T, typename = void>
102struct has_cpp_meta_desc : std::false_type {};
103
104template <typename T>
105struct has_cpp_meta_desc<T, decltype(void(
106 flecs_meta_cpp_desc(static_cast<T*>(nullptr))))> : std::true_type {};
107#endif
108
109template <typename T>
110inline ecs_cpp_type_action_t enum_action() {
111#if FLECS_CPP_ENUM_REFLECTION_SUPPORT
112#ifdef FLECS_META
113 if constexpr (has_cpp_meta_desc<T>::value) {
114 return nullptr;
115 } else
116#endif
117 if constexpr (is_enum_v<T>) {
118 return &_::init_enum<T>;
119 } else {
120 return nullptr;
121 }
122#else
123 return nullptr;
124#endif
125}
126
127#ifdef FLECS_META
128
129template <typename T>
130inline void register_cpp_meta(ecs_world_t *world, ecs_entity_t component) {
131 (void)world; (void)component;
132 if constexpr (has_cpp_meta_desc<T>::value) {
133 ecs_type_kind_t kind = flecs_meta_cpp_kind(static_cast<T*>(nullptr));
134 if (kind == EcsStructType && ecs_has_id(world, component,
135 ecs_id(EcsStruct))) {
136 return;
137 }
138 if (kind == EcsEnumType && ecs_has_id(world, component,
139 ecs_id(EcsEnum))) {
140 return;
141 }
142 if (kind == EcsBitmaskType && ecs_has_id(world, component,
143 ecs_id(EcsBitmask))) {
144 return;
145 }
146 ecs_meta_from_desc(world, component, kind,
147 flecs_meta_cpp_desc(static_cast<T*>(nullptr)));
148 }
149}
150#endif
151
152template <typename T>
153struct type_impl {
154 static_assert(is_pointer<T>::value == false,
155 "pointer types are not allowed for components");
156
157 // Initialize the component identifier.
158 static void init(
159 bool allow_tag = true)
160 {
161 index(); // Make sure the global component index is initialized.
162
163 s_size = sizeof(T);
164 s_alignment = alignof(T);
165 if (is_empty<T>::value && allow_tag) {
166 s_size = 0;
167 s_alignment = 0;
168 }
169 }
170
171 static void init_builtin(
174 bool allow_tag = true)
175 {
176 init(allow_tag);
177 flecs_component_ids_set(world, index(), id);
178 }
179
180 // Register the component ID.
181 static entity_t register_id(
182 world_t *world, // The world
183 const char *name = nullptr, // User-provided name (overrides typename)
184 bool allow_tag = true, // Register empty types as zero-sized components
185 bool is_component = true, // Add flecs::Component to the result
186 bool explicit_registration = false, // Entered from world.component<T>()?
187 flecs::id_t id = 0) // User-provided component ID
188 {
189 init(allow_tag);
190 ecs_assert(index() != 0, ECS_INTERNAL_ERROR, NULL);
191
192 ecs_cpp_component_desc_t desc = {
193 id,
194 index(),
195 name,
196 type_name<T>(),
197 component_symbol_name<T>(),
198 size(),
199 alignment(),
200 lifecycle_action<T>(),
201 enum_action<T>(),
202 is_component,
203 explicit_registration
204 };
205
206 flecs::entity_t c = ecs_cpp_component_register(world, &desc);
207
208 ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL);
209
210#ifdef FLECS_META
211 register_cpp_meta<T>(world, c);
212#endif
213
214 return c;
215 }
216
217 // Get the type (component) ID.
218 // If the type was not yet registered and automatic registration is allowed,
219 // this function will also register the type.
220 static entity_t id(world_t *world)
221 {
222#ifdef FLECS_CPP_NO_AUTO_REGISTRATION
224 "component '%s' must be registered before use",
225 type_name<T>());
226
227 flecs::entity_t c = flecs_component_ids_get(world, index());
228 ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL);
230 "component '%s' was deleted, reregister before using",
231 type_name<T>());
232#else
233 flecs::entity_t c = flecs_component_ids_get_alive(world, index());
234 if (!c) {
235 c = register_id(world);
236 }
237#endif
238 return c;
239 }
240
241 // Return the size of a component.
242 static size_t size() {
243 return s_size;
244 }
245
246 // Return the alignment of a component.
247 static size_t alignment() {
248 return s_alignment;
249 }
250
251 // Was the component already registered?
252 static bool registered(flecs::world_t *world) {
253 ecs_assert(world != nullptr, ECS_INVALID_PARAMETER, NULL);
254
255 if (!flecs_component_ids_get(world, index())) {
256 return false;
257 }
258
259 return true;
260 }
261
262 // This function is only used to test cross-translation-unit features. No
263 // code other than test cases should invoke this function.
264 static void reset() {
265 s_size = 0;
266 s_alignment = 0;
267 }
268
269 static int32_t index() {
270 static int32_t index_ = flecs_component_ids_index_get();
271 return index_;
272 }
273
274 static size_t s_size;
275 static size_t s_alignment;
276};
277
278// Global templated variables that hold the component identifier and other info.
279template <typename T> inline size_t type_impl<T>::s_size;
280template <typename T> inline size_t type_impl<T>::s_alignment;
281
282// Front-facing class for implicitly registering a component and obtaining
283// static component data.
284
285// Regular type.
286template <typename T>
287struct type<T, if_not_t< is_pair<T>::value >>
288 : type_impl<base_type_t<T>> { };
289
290// Pair type.
291template <typename T>
292struct type<T, if_t< is_pair<T>::value >>
293{
294 // Override the id() method to return the ID of a pair.
295 static id_t id(world_t *world = nullptr) {
296 return ecs_pair(
297 type< pair_first_t<T> >::id(world),
298 type< pair_second_t<T> >::id(world));
299 }
300};
301
302} // namespace _
303
310 using entity::entity;
311
314
321
327
334 {
335 world_ = world;
336
337 ecs_entity_desc_t desc = {};
338 desc.name = name;
339 desc.sep = "::";
340 desc.root_sep = "::";
341 desc.use_low_id = true;
342 id_ = ecs_entity_init(world, &desc);
343 }
344
352 explicit untyped_component(world_t *world, const char *name, const char *sep, const char *root_sep)
353 {
354 world_ = world;
355
356 ecs_entity_desc_t desc = {};
357 desc.name = name;
358 desc.sep = sep;
359 desc.root_sep = root_sep;
360 desc.use_low_id = true;
361 id_ = ecs_entity_init(world, &desc);
362 }
363
364protected:
365
372 if (h) {
373 return *h;
374 } else {
375 return {};
376 }
377 }
378
384 h.flags &= ECS_TYPE_HOOKS_ILLEGAL;
386 }
387
388public:
389
396 ecs_cmp_t compare_callback)
397{
398 ecs_assert(compare_callback, ECS_INVALID_PARAMETER, NULL);
400 h.cmp = compare_callback;
401 h.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL;
402 if(h.flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) {
403 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
404 h.equals = NULL;
405 }
406 set_hooks(h);
407 return *this;
408}
409
416 ecs_equals_t equals_callback)
417{
418 ecs_assert(equals_callback, ECS_INVALID_PARAMETER, NULL);
420 h.equals = equals_callback;
421 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
422 set_hooks(h);
423 return *this;
424}
425
426# ifdef FLECS_META
428# endif
429# ifdef FLECS_METRICS
431# endif
432};
433
439template <typename T>
452 const char *name = nullptr,
453 bool allow_tag = true,
454 flecs::id_t id = 0)
455 {
456 world_ = world;
457 id_ = _::type<T>::register_id(world, name, allow_tag, true, true, id);
458 }
459
465 template <typename Func>
466 component<T>& on_add(Func&& func) {
467 using Delegate = typename _::each_delegate<typename std::decay<Func>::type, T>;
470 "on_add hook is already set");
471 BindingCtx *ctx = get_binding_ctx(h);
472 h.on_add = Delegate::run_add;
473 ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func));
474 ctx->free_on_add = _::free_obj<Delegate>;
475 set_hooks(h);
476 return *this;
477 }
478
484 template <typename Func>
485 component<T>& on_remove(Func&& func) {
486 using Delegate = typename _::each_delegate<
487 typename std::decay<Func>::type, T>;
490 "on_remove hook is already set");
491 BindingCtx *ctx = get_binding_ctx(h);
492 h.on_remove = Delegate::run_remove;
493 ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func));
494 ctx->free_on_remove = _::free_obj<Delegate>;
495 set_hooks(h);
496 return *this;
497 }
498
504 template <typename Func>
505 component<T>& on_set(Func&& func) {
506 using Delegate = typename _::each_delegate<
507 typename std::decay<Func>::type, T>;
510 "on_set hook is already set");
511 BindingCtx *ctx = get_binding_ctx(h);
512 h.on_set = Delegate::run_set;
513 ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func));
514 ctx->free_on_set = _::free_obj<Delegate>;
515 set_hooks(h);
516 return *this;
517 }
518
524 template <typename Func>
525 component<T>& on_replace(Func&& func) {
526 using Delegate = typename _::each_delegate<
527 typename std::decay<Func>::type, T, T>;
530 "on_replace hook is already set");
531 BindingCtx *ctx = get_binding_ctx(h);
532 h.on_replace = Delegate::run_replace;
533 ctx->on_replace = FLECS_NEW(Delegate)(FLECS_FWD(func));
534 ctx->free_on_replace = _::free_obj<Delegate>;
535 set_hooks(h);
536 return *this;
537 }
538
540
546 ecs_cmp_t handler = _::compare<T>();
547 ecs_assert(handler != NULL, ECS_INVALID_OPERATION,
548 "Type does not have operator> or operator< const or is inaccessible");
549 on_compare(handler);
550 return *this;
551 }
552
553 using cmp_hook = int(*)(const T* a, const T* b, const ecs_type_info_t *ti);
554
560 component<T>& on_compare(cmp_hook callback) {
561 on_compare(reinterpret_cast<ecs_cmp_t>(callback));
562 return *this;
563 }
564
566
572 ecs_equals_t handler = _::equals<T>();
573 ecs_assert(handler != NULL, ECS_INVALID_OPERATION,
574 "Type does not have operator== const or is inaccessible");
575 on_equals(handler);
576 return *this;
577 }
578
579 using equals_hook = bool(*)(const T* a, const T* b, const ecs_type_info_t *ti);
580
586 component<T>& on_equals(equals_hook callback) {
587 on_equals(reinterpret_cast<ecs_equals_t>(callback));
588 return *this;
589 }
590
591# ifdef FLECS_META
593# endif
594
595private:
596 using BindingCtx = _::component_binding_ctx;
597
598 BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){
599 BindingCtx *result = static_cast<BindingCtx*>(h.binding_ctx);
600 if (!result) {
601 result = FLECS_NEW(BindingCtx);
602 h.binding_ctx = result;
603 h.binding_ctx_free = _::free_obj<BindingCtx>;
604 }
605 return result;
606 }
607};
608
609}
610
Meta component mixin.
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:151
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:385
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:429
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.
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:683
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:689
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:305
Component added to enum type entities.
Definition meta.h:285
Component added to struct type entities.
Definition meta.h:264
Used with ecs_entity_init().
Definition flecs.h:1046
const char * sep
Optional custom separator for hierarchical names.
Definition flecs.h:1058
const char * root_sep
Optional, used for identifiers relative to the root.
Definition flecs.h:1062
const char * name
Name of the entity.
Definition flecs.h:1053
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:1074
ecs_iter_action_t on_remove
Callback that is invoked when an instance of the component is removed.
Definition flecs.h:1002
void * binding_ctx
Language binding context.
Definition flecs.h:1011
ecs_flags32_t flags
Hook flags.
Definition flecs.h:988
ecs_cmp_t cmp
Compare hook.
Definition flecs.h:979
ecs_iter_action_t on_set
Callback that is invoked when an instance of the component is set.
Definition flecs.h:997
ecs_xtor_t ctor
ctor.
Definition flecs.h:955
ecs_iter_action_t on_replace
Callback that is invoked with the existing and new value before the value is assigned.
Definition flecs.h:1008
ecs_iter_action_t on_add
Callback that is invoked when an instance of a component is added.
Definition flecs.h:992
ecs_ctx_free_t binding_ctx_free
Callback to free binding_ctx.
Definition flecs.h:1015
ecs_equals_t equals
Equals hook.
Definition flecs.h:982
Type that contains component information (passed to ctors/dtors/...).
Definition flecs.h:1023
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_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.
flecs::string_view name() const
Return the entity name.
Entity.
Definition entity.hpp:30
entity()
Default constructor.
Definition entity.hpp:32
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
flecs::world world() const
Get the world.
Definition impl.hpp:58
flecs::id_t id_
The raw ID value.
Definition decl.hpp:149
flecs::world_t * world_
World is optional, but guarantees that entity identifiers extracted from the ID are valid.
Definition decl.hpp:147
Test if a type is a pair.
Definition pair.hpp:98
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:246
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