Flecs: include/flecs/addons/cpp/component.hpp Source File
Description: A fast entity component system (ECS) for C & C++

| 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
5
6#pragma once
7
9 * @defgroup cpp_components Components
10 * @ingroup cpp_core
11 * Registering and working with components.
12 *
13 * @{
14 */
15
16namespace flecs {
17
18 namespace _ {
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}
28 template <> 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}
34 template <> 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(
62 ecs_world_t *world,
63 ecs_entity_t component)
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.
69 ecs_type_hooks_t cl{};
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;
82 ecs_set_hooks_id(world, component, &cl);
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>
92 inline 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
100template <typename T>
101inline ecs_cpp_type_action_t enum_action() {
102#if FLECS_CPP_ENUM_REFLECTION_SUPPORT
103 if constexpr (is_enum_v<T>) {
104 return &_::init_enum<T>;
105 }
106#endif
107 return nullptr;
108}
109
110template <typename T>
111 struct type_impl {
112 static_assert(is_pointer<T>::value == false,
113 "pointer types are not allowed for components");
114
115 // Initialize the component identifier.
116 static void init(
117 bool allow_tag = true)
118 {
119 index(); // Make sure the global component index is initialized.
120
121 s_size = sizeof(T);
122 s_alignment = alignof(T);
123 if (is_empty<T>::value && allow_tag) {
124 s_size = 0;
125 s_alignment = 0;
126 }
127 }
128
129 static void init_builtin(
130 flecs::world_t *world,
131 flecs::entity_t id,
132 bool allow_tag = true)
133 {
134 init(allow_tag);
135 flecs_component_ids_set(world, index(), id);
136 }
137
138 // Register the component ID.
139 static entity_t register_id(
140 world_t *world, // The world
141 const char *name = nullptr, // User-provided name (overrides typename)
142 bool allow_tag = true, // Register empty types as zero-sized components
143 bool is_component = true, // Add flecs::Component to the result
144 bool explicit_registration = false, // Entered from world.component<T>()?
145 flecs::id_t id = 0) // User-provided component ID
146 {
147 init(allow_tag);
148 ecs_assert(index() != 0, ECS_INTERNAL_ERROR, NULL);
149
150 ecs_cpp_component_desc_t desc = {
151 id,
152 index(),
153 name,
154 type_name<T>(),
155 component_symbol_name<T>(),
156 size(),
157 alignment(),
158 lifecycle_action<T>(),
159 enum_action<T>(),
160 is_component,
161 explicit_registration
162 };
163
164 flecs::entity_t c = ecs_cpp_component_register(world, &desc);
165
166 ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL);
167
168 return c;
169 }
170
171 // Get the type (component) ID.
172 // If the type was not yet registered and automatic registration is allowed,
173 // this function will also register the type.
174 static entity_t id(world_t *world)
175 {
176#ifdef FLECS_CPP_NO_AUTO_REGISTRATION
177 ecs_assert(registered(world), ECS_INVALID_OPERATION,
178 "component '%s' must be registered before use",
179 type_name<T>());
180
181 flecs::entity_t c = flecs_component_ids_get(world, index());
182 ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL);
183 ecs_assert(ecs_is_alive(world, c), ECS_INVALID_OPERATION,
184 "component '%s' was deleted, reregister before using",
185 type_name<T>());
186#else
187 flecs::entity_t c = flecs_component_ids_get_alive(world, index());
188 if (!c) {
189 c = register_id(world);
190 }
191#endif
192 return c;
193 }
194
195 // Return the size of a component.
196 static size_t size() {
197 return s_size;
198 }
199
200 // Return the alignment of a component.
201 static size_t alignment() {
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 (!flecs_component_ids_get(world, index())) {
210 return false;
211 }
212
213 return true;
214 }
215
216 // This function is only used to test cross-translation-unit features. No
217 // code other than test cases should invoke this function.
218 static void reset() {
219 s_size = 0;
220 s_alignment = 0;
221 }
222
223 static int32_t index() {
224 static int32_t index_ = flecs_component_ids_index_get();
225 return index_;
226 }
227
228 static size_t s_size;
229 static size_t s_alignment;
230};
231
232// Global templated variables that hold the component identifier and other info.
233template <typename T> inline size_t type_impl<T>::s_size;
234template <typename T> inline size_t type_impl<T>::s_alignment;
235
236// Front-facing class for implicitly registering a component and obtaining
237// static component data.
238
239// Regular type.
240template <typename T>
241 struct type<T, if_not_t< is_pair<T>::value >>
242 : type_impl<base_type_t<T>> { };
243
244// Pair type.
245template <typename T>
246 struct type<T, if_t< is_pair<T>::value >>
247{
248 // Override the id() method to return the ID of a pair.
249 static id_t id(world_t *world = nullptr) {
250 return ecs_pair(
251 type< pair_first_t<T> >::id(world),
252 type< pair_second_t<T> >::id(world));
253 }
254};
255
256} // namespace _
257
263 struct untyped_component : entity {
264 using entity::entity;
265
267 untyped_component() : entity() { }
268
274 explicit untyped_component(flecs::world_t *world, flecs::entity_t id) : entity(world, id) { }
275
280 explicit untyped_component(flecs::entity_t id) : entity(id) { }
281
287 explicit untyped_component(flecs::world_t *world, const char *name)
288 {
289 world_ = world;
290
291 ecs_entity_desc_t desc = {};
292 desc.name = name;
293 desc.sep = "::";
294 desc.root_sep = "::";
295 desc.use_low_id = true;
296 id_ = ecs_entity_init(world, &desc);
297 }
298
306 explicit untyped_component(world_t *world, const char *name, const char *sep, const char *root_sep)
307 {
308 world_ = world;
309
310 ecs_entity_desc_t desc = {};
311 desc.name = name;
312 desc.sep = sep;
313 desc.root_sep = root_sep;
314 desc.use_low_id = true;
315 id_ = ecs_entity_init(world, &desc);
316 }
317
318protected:
319
320
324 flecs::type_hooks_t get_hooks() const {
325 const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_);
326 if (h) {
327 return *h;
328 } else {
329 return {};
330 }
331 }
332
337 void set_hooks(flecs::type_hooks_t &h) {
338 h.flags &= ECS_TYPE_HOOKS_ILLEGAL;
339 ecs_set_hooks_id(world_, id_, &h);
340 }
341
342public:
343
349 untyped_component& on_compare(
350 ecs_cmp_t compare_callback)
351{
352 ecs_assert(compare_callback, ECS_INVALID_PARAMETER, NULL);
353 flecs::type_hooks_t h = get_hooks();
354 h.cmp = compare_callback;
355 h.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL;
356 if(h.flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) {
357 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
358 h.equals = NULL;
359 }
360 set_hooks(h);
361 return *this;
362}
363
369 untyped_component& on_equals(
370 ecs_equals_t equals_callback)
371{
372 ecs_assert(equals_callback, ECS_INVALID_PARAMETER, NULL);
373 flecs::type_hooks_t h = get_hooks();
374 h.equals = equals_callback;
375 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
376 set_hooks(h);
377 return *this;
378}
379
380# ifdef FLECS_META
381# include "mixins/meta/untyped_component.inl"
382# endif
383# ifdef FLECS_METRICS
384# include "mixins/metrics/untyped_component.inl"
385# endif
386};
387
393template <typename T>
394 struct component : untyped_component {
404 component(
405 flecs::world_t *world,
406 const char *name = nullptr,
407 bool allow_tag = true,
408 flecs::id_t id = 0)
409 {
410 world_ = world;
411 id_ = _::type<T>::register_id(world, name, allow_tag, true, true, id);
412 }
413
419 template <typename Func>
420 component<T>& on_add(Func&& func) {
421 using Delegate = typename _::each_delegate<typename std::decay<Func>::type, T>;
422 flecs::type_hooks_t h = get_hooks();
423 ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION,
424 "on_add hook is already set");
425 BindingCtx *ctx = get_binding_ctx(h);
426 h.on_add = Delegate::run_add;
427 ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func));
428 ctx->free_on_add = _::free_obj<Delegate>;
429 set_hooks(h);
430 return *this;
431 }
432
438 template <typename Func>
439 component<T>& on_remove(Func&& func) {
440 using Delegate = typename _::each_delegate<
441 typename std::decay<Func>::type, T>;
442 flecs::type_hooks_t h = get_hooks();
443 ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION,
444 "on_remove hook is already set");
445 BindingCtx *ctx = get_binding_ctx(h);
446 h.on_remove = Delegate::run_remove;
447 ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func));
448 ctx->free_on_remove = _::free_obj<Delegate>;
449 set_hooks(h);
450 return *this;
451 }
452
458 template <typename Func>
459 component<T>& on_set(Func&& func) {
460 using Delegate = typename _::each_delegate<
461 typename std::decay<Func>::type, T>;
462 flecs::type_hooks_t h = get_hooks();
463 ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION,
464 "on_set hook is already set");
465 BindingCtx *ctx = get_binding_ctx(h);
466 h.on_set = Delegate::run_set;
467 ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func));
468 ctx->free_on_set = _::free_obj<Delegate>;
469 set_hooks(h);
470 return *this;
471 }
472
478 template <typename Func>
479 component<T>& on_replace(Func&& func) {
480 using Delegate = typename _::each_delegate<
481 typename std::decay<Func>::type, T, T>;
482 flecs::type_hooks_t h = get_hooks();
483 ecs_assert(h.on_replace == nullptr, ECS_INVALID_OPERATION,
484 "on_replace hook is already set");
485 BindingCtx *ctx = get_binding_ctx(h);
486 h.on_replace = Delegate::run_replace;
487 ctx->on_replace = FLECS_NEW(Delegate)(FLECS_FWD(func));
488 ctx->free_on_replace = _::free_obj<Delegate>;
489 set_hooks(h);
490 return *this;
491 }
492
493 using untyped_component::on_compare;
494
499 component<T>& on_compare() {
500 ecs_cmp_t handler = _::compare<T>();
501 ecs_assert(handler != NULL, ECS_INVALID_OPERATION,
502 "Type does not have operator> or operator< const or is inaccessible");
503 on_compare(handler);
504 return *this;
505 }
506
507 using cmp_hook = int(*)(const T* a, const T* b, const ecs_type_info_t *ti);
508
514 component<T>& on_compare(cmp_hook callback) {
515 on_compare(reinterpret_cast<ecs_cmp_t>(callback));
516 return *this;
517 }
518
519 using untyped_component::on_equals;
520
525 component<T>& on_equals() {
526 ecs_equals_t handler = _::equals<T>();
527 ecs_assert(handler != NULL, ECS_INVALID_OPERATION,
528 "Type does not have operator== const or is inaccessible");
529 on_equals(handler);
530 return *this;
531 }
532
533 using equals_hook = bool(*)(const T* a, const T* b, const ecs_type_info_t *ti);
534
540 component<T>& on_equals(equals_hook callback) {
541 on_equals(reinterpret_cast<ecs_equals_t>(callback));
542 return *this;
543 }
544
545# ifdef FLECS_META
546# include "mixins/meta/component.inl"
547# endif
548
549private:
550 using BindingCtx = _::component_binding_ctx;
551
552 BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){
553 BindingCtx *result = static_cast<BindingCtx*>(h.binding_ctx);
554 if (!result) {
555 result = FLECS_NEW(BindingCtx);
556 h.binding_ctx = result;
557 h.binding_ctx_free = _::free_obj<BindingCtx>;
558 }
559 return result;
560 }
561};
562
563}
564
component.inl
Meta component mixin.
ecs_add_id
void ecs_add_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t component)
Add a (component) ID to an entity.
ecs_assert
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
ECS_INVALID_OPERATION
#define ECS_INVALID_OPERATION
Invalid operation error code.
Definition log.h:659
ECS_INVALID_PARAMETER
#define ECS_INVALID_PARAMETER
Invalid parameter error code.
Definition log.h:661
ECS_INTERNAL_ERROR
#define ECS_INTERNAL_ERROR
Internal error code.
Definition log.h:671
ecs_get_hooks_id
const ecs_type_hooks_t * ecs_get_hooks_id(const ecs_world_t *world, ecs_entity_t component)
Get hooks for a component.
ecs_set_hooks_id
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_entity_t
ecs_id_t ecs_entity_t
An entity identifier.
Definition flecs.h:381
ecs_world_t
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:425
flecs::type_hooks_t
ecs_type_hooks_t type_hooks_t
Type hooks type.
Definition c_types.hpp:34
flecs::id_t
ecs_id_t id_t
ID type.
Definition c_types.hpp:20
flecs::entity_t
ecs_entity_t entity_t
Entity type.
Definition c_types.hpp:21
flecs::world_t
ecs_world_t world_t
World type.
Definition c_types.hpp:18
flecs::pair_second_t
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
flecs::pair_first_t
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_init
ecs_entity_t ecs_entity_init(ecs_world_t *world, const ecs_entity_desc_t *desc)
Find or create an entity.
ecs_cmp_t
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:679
ecs_equals_t
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:685
ecs_is_alive
bool ecs_is_alive(const ecs_world_t *world, ecs_entity_t e)
Test whether an entity is alive.
untyped_component.inl
Meta component mixin.
untyped_component.inl
Metrics component mixin.
flecs::_
Int to enum.
Definition component.hpp:18
ecs_entity_desc_t
Used with ecs_entity_init().
Definition flecs.h:1042
ecs_entity_desc_t::sep
const char * sep
Optional custom separator for hierarchical names.
Definition flecs.h:1054
ecs_entity_desc_t::root_sep
const char * root_sep
Optional, used for identifiers relative to the root.
Definition flecs.h:1058
ecs_entity_desc_t::name
const char * name
Name of the entity.
Definition flecs.h:1049
ecs_entity_desc_t::use_low_id
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:1070
ecs_type_hooks_t
Definition flecs.h:950
ecs_type_hooks_t::copy_ctor
ecs_copy_t copy_ctor
Ctor + copy.
Definition flecs.h:957
ecs_type_hooks_t::on_remove
ecs_iter_action_t on_remove
Callback that is invoked when an instance of the component is removed.
Definition flecs.h:998
ecs_type_hooks_t::binding_ctx
void * binding_ctx
Language binding context.
Definition flecs.h:1007
ecs_type_hooks_t::move_dtor
ecs_move_t move_dtor
Move + dtor.
Definition flecs.h:972
ecs_type_hooks_t::flags
ecs_flags32_t flags
Hook flags.
Definition flecs.h:984
ecs_type_hooks_t::cmp
ecs_cmp_t cmp
Compare hook.
Definition flecs.h:975
ecs_type_hooks_t::copy
ecs_copy_t copy
copy assignment.
Definition flecs.h:953
ecs_type_hooks_t::on_set
ecs_iter_action_t on_set
Callback that is invoked when an instance of the component is set.
Definition flecs.h:993
ecs_type_hooks_t::move
ecs_move_t move
move assignment.
Definition flecs.h:954
ecs_type_hooks_t::ctor
ecs_xtor_t ctor
ctor.
Definition flecs.h:951
ecs_type_hooks_t::on_replace
ecs_iter_action_t on_replace
Callback that is invoked with the existing and new value before the value is assigned.
Definition flecs.h:1004
ecs_type_hooks_t::on_add
ecs_iter_action_t on_add
Callback that is invoked when an instance of a component is added.
Definition flecs.h:988
ecs_type_hooks_t::binding_ctx_free
ecs_ctx_free_t binding_ctx_free
Callback to free binding_ctx.
Definition flecs.h:1011
ecs_type_hooks_t::move_ctor
ecs_move_t move_ctor
Ctor + move.
Definition flecs.h:960
ecs_type_hooks_t::equals
ecs_equals_t equals
Equals hook.
Definition flecs.h:978
ecs_type_hooks_t::ctor_move_dtor
ecs_move_t ctor_move_dtor
Ctor + move + dtor (or move_ctor + dtor).
Definition flecs.h:966
ecs_type_hooks_t::dtor
ecs_xtor_t dtor
dtor.
Definition flecs.h:952
ecs_type_info_t
Type that contains component information (passed to ctors/dtors/...).
Definition flecs.h:1019
flecs::_::component_binding_ctx
Definition delegate.hpp:17
flecs::_::each_delegate
Definition delegate.hpp:220
flecs::_::type_impl
Definition component.hpp:111
flecs::_::type
Definition flecs.hpp:39
flecs::component
Component class.
Definition component.hpp:394
flecs::component::on_remove
component< T > & on_remove(Func &&func)
Register on_remove hook.
Definition component.hpp:439
flecs::component::component
component(flecs::world_t *world, const char *name=nullptr, bool allow_tag=true, flecs::id_t id=0)
Register a component.
Definition component.hpp:404
flecs::component::on_replace
component< T > & on_replace(Func &&func)
Register on_replace hook.
Definition component.hpp:479
flecs::component::on_compare
component< T > & on_compare(cmp_hook callback)
Type-safe variant of the compare op function.
Definition component.hpp:514
flecs::component::on_add
component< T > & on_add(Func &&func)
Register on_add hook.
Definition component.hpp:420
flecs::component::on_equals
component< T > & on_equals()
Register an operator equals hook using type T's equality operator.
Definition component.hpp:525
flecs::component::on_set
component< T > & on_set(Func &&func)
Register on_set hook.
Definition component.hpp:459
flecs::component::on_equals
component< T > & on_equals(equals_hook callback)
Type-safe variant of the equals op function.
Definition component.hpp:540
flecs::component::on_compare
component< T > & on_compare()
Register an operator compare hook using type T's comparison operators.
Definition component.hpp:499
flecs::entity_view::name
flecs::string_view name() const
Return the entity name.
Definition entity_view.hpp:88
flecs::entity::entity
entity()
Default constructor.
Definition entity.hpp:32
flecs::id
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
flecs::id::id_
flecs::id_t id_
The raw ID value.
Definition decl.hpp:149
flecs::id::world_
flecs::world_t * world_
World is optional, but guarantees that entity identifiers extracted from the ID are valid.
Definition decl.hpp:147
flecs::is_pair
Test if a type is a pair.
Definition pair.hpp:98
flecs::untyped_component::untyped_component
untyped_component(flecs::world_t *world, flecs::entity_t id)
Construct from world and entity ID.
Definition component.hpp:274
flecs::untyped_component::untyped_component
untyped_component(world_t *world, const char *name, const char *sep, const char *root_sep)
Construct from world, name, and scope separators.
Definition component.hpp:306
flecs::untyped_component::on_compare
untyped_component & on_compare(ecs_cmp_t compare_callback)
Register a custom compare hook for this component.
Definition component.hpp:349
flecs::untyped_component::get_hooks
flecs::type_hooks_t get_hooks() const
Get the type hooks for this component.
Definition component.hpp:324
flecs::untyped_component::untyped_component
untyped_component()
Default constructor.
Definition component.hpp:267
flecs::untyped_component::untyped_component
untyped_component(flecs::entity_t id)
Construct from entity ID.
Definition component.hpp:280
flecs::untyped_component::set_hooks
void set_hooks(flecs::type_hooks_t &h)
Set the type hooks for this component.
Definition component.hpp:337
flecs::untyped_component::untyped_component
untyped_component(flecs::world_t *world, const char *name)
Construct from world and name.
Definition component.hpp:287
flecs::untyped_component::entity
entity()
Default constructor.
Definition entity.hpp:32
flecs::untyped_component::on_equals
untyped_component & on_equals(ecs_equals_t equals_callback)
Register a custom equals hook for this component.
Definition component.hpp:369
flecs::world
The world.
Definition world.hpp:246
flecs::if_not_t
enable_if_t< false==V, int > if_not_t
Convenience enable_if alias for negated conditions.
Definition utils.hpp:172
flecs::if_t
enable_if_t< V, int > if_t
Convenience enable_if alias using int as default type.
Definition utils.hpp:168