Flecs v4.0
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
8#include <ctype.h>
9#include <stdio.h>
10
19namespace flecs {
20
21namespace _ {
22
23// Trick to obtain typename from type, as described here
24// https://blog.molecular-matters.com/2015/12/11/getting-the-type-of-a-template-argument-as-string-without-rtti/
25//
26// The code from the link has been modified to work with more types, and across
27// multiple compilers. The resulting string should be the same on all platforms
28// for all compilers.
29//
30
31#if defined(__GNUC__) || defined(_WIN32)
32template <typename T>
33inline const char* type_name() {
34 static const size_t len = ECS_FUNC_TYPE_LEN(const char*, type_name, ECS_FUNC_NAME);
35 static char result[len + 1] = {};
36 static const size_t front_len = ECS_FUNC_NAME_FRONT(const char*, type_name);
37 static const char* cppTypeName = ecs_cpp_get_type_name(result, ECS_FUNC_NAME, len, front_len);
38 return cppTypeName;
39}
40#else
41#error "implicit component registration not supported"
42#endif
43
44// Translate a typename into a language-agnostic identifier. This allows for
45// registration of components/modules across language boundaries.
46template <typename T>
47inline const char* symbol_name() {
48 static const size_t len = ECS_FUNC_TYPE_LEN(const char*, symbol_name, ECS_FUNC_NAME);
49 static char result[len + 1] = {};
50 static const char* cppSymbolName = ecs_cpp_get_symbol_name(result, type_name<T>(), len);
51 return cppSymbolName;
52}
53
54template <> inline const char* symbol_name<uint8_t>() {
55 return "u8";
56}
57template <> inline const char* symbol_name<uint16_t>() {
58 return "u16";
59}
60template <> inline const char* symbol_name<uint32_t>() {
61 return "u32";
62}
63template <> inline const char* symbol_name<uint64_t>() {
64 return "u64";
65}
66template <> inline const char* symbol_name<int8_t>() {
67 return "i8";
68}
69template <> inline const char* symbol_name<int16_t>() {
70 return "i16";
71}
72template <> inline const char* symbol_name<int32_t>() {
73 return "i32";
74}
75template <> inline const char* symbol_name<int64_t>() {
76 return "i64";
77}
78template <> inline const char* symbol_name<float>() {
79 return "f32";
80}
81template <> inline const char* symbol_name<double>() {
82 return "f64";
83}
84
85// If type is trivial, don't register lifecycle actions. While the functions
86// that obtain the lifecycle callback do detect whether the callback is required
87// adding a special case for trivial types eases the burden a bit on the
88// compiler as it reduces the number of templates to evaluate.
89template<typename T, enable_if_t<
90 std::is_trivial<T>::value == true
91 >* = nullptr>
92void register_lifecycle_actions(ecs_world_t*, ecs_entity_t) { }
93
94// If the component is non-trivial, register component lifecycle actions.
95// Depending on the type not all callbacks may be available.
96template<typename T, enable_if_t<
97 std::is_trivial<T>::value == false
98 >* = nullptr>
99void register_lifecycle_actions(
102{
103 ecs_type_hooks_t cl{};
104 cl.ctor = ctor<T>(cl.flags);
105 cl.dtor = dtor<T>(cl.flags);
106
107 cl.copy = copy<T>(cl.flags);
108 cl.copy_ctor = copy_ctor<T>(cl.flags);
109 cl.move = move<T>(cl.flags);
110 cl.move_ctor = move_ctor<T>(cl.flags);
111
112 cl.ctor_move_dtor = ctor_move_dtor<T>(cl.flags);
113 cl.move_dtor = move_dtor<T>(cl.flags);
114
115 cl.flags &= ECS_TYPE_HOOKS_ILLEGAL;
117
118 if (cl.flags & (ECS_TYPE_HOOK_MOVE_ILLEGAL|ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL))
119 {
120 ecs_add_id(world, component, flecs::Sparse);
121 }
122}
123
124template <typename T>
125struct type_impl {
126 static_assert(is_pointer<T>::value == false,
127 "pointer types are not allowed for components");
128
129 // Initialize component identifier
130 static void init(
131 bool allow_tag = true)
132 {
133 s_index = flecs_component_ids_index_get();
134 s_allow_tag = allow_tag;
135 s_size = sizeof(T);
136 s_alignment = alignof(T);
137 if (is_empty<T>::value && allow_tag) {
138 s_size = 0;
139 s_alignment = 0;
140 }
141 }
142
143 static void init_builtin(
144 flecs::world_t *world,
145 flecs::entity_t id,
146 bool allow_tag = true)
147 {
148 init(allow_tag);
149 flecs_component_ids_set(world, s_index, id);
150 }
151
152 // Register component id.
153 static entity_t register_id(
154 world_t *world, // The world
155 const char *name = nullptr, // User provided name (overrides typename)
156 bool allow_tag = true, // Register empty types as zero-sized components
157 bool is_component = true, // Add flecs::Component to result
158 bool explicit_registration = false, // Entered from world.component<T>()?
159 flecs::id_t id = 0) // User provided component id
160 {
161 if (!s_index) {
162 // This is the first time (in this binary image) that this type is
163 // being used. Generate a static index that will identify the type
164 // across worlds.
165 init(allow_tag);
166 ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL);
167 }
168
169 bool registered = false, existing = false;
170
171 flecs::entity_t c = ecs_cpp_component_register(
172 world, id, s_index, name, type_name<T>(),
173 symbol_name<T>(), size(), alignment(),
174 is_component, explicit_registration, &registered, &existing);
175
176 ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL);
177
178 if (registered) {
179 // Register lifecycle callbacks, but only if the component has a
180 // size. Components that don't have a size are tags, and tags don't
181 // require construction/destruction/copy/move's.
182 if (size() && !existing) {
183 register_lifecycle_actions<T>(world, c);
184 }
185
186 // If component is enum type, register constants. Make sure to do
187 // this after setting the component id, because the enum code will
188 // be calling type<T>::id().
189 #if FLECS_CPP_ENUM_REFLECTION_SUPPORT
190 _::init_enum<T>(world, c);
191 #endif
192 }
193
194 return c;
195 }
196
197 // Get type (component) id.
198 // If type was not yet registered and automatic registration is allowed,
199 // this function will also register the type.
200 static entity_t id(world_t *world)
201 {
202#ifdef FLECS_CPP_NO_AUTO_REGISTRATION
203 ecs_assert(registered(world), ECS_INVALID_OPERATION,
204 "component '%s' must be registered before use",
205 type_name<T>());
206
207 flecs::entity_t c = flecs_component_ids_get(world, s_index);
208 ecs_assert(c != 0, ECS_INTERNAL_ERROR, NULL);
209 ecs_assert(ecs_is_alive(world, c), ECS_INVALID_OPERATION,
210 "component '%s' was deleted, reregister before using",
211 type_name<T>());
212#else
213 flecs::entity_t c = flecs_component_ids_get_alive(world, s_index);
214 if (!c) {
215 c = register_id(world);
216 }
217#endif
218 return c;
219 }
220
221 // Return the size of a component.
222 static size_t size() {
223 ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL);
224 return s_size;
225 }
226
227 // Return the alignment of a component.
228 static size_t alignment() {
229 ecs_assert(s_index != 0, ECS_INTERNAL_ERROR, NULL);
230 return s_alignment;
231 }
232
233 // Was the component already registered.
234 static bool registered(flecs::world_t *world) {
235 ecs_assert(world != nullptr, ECS_INVALID_PARAMETER, NULL);
236
237 if (s_index == 0) {
238 return false;
239 }
240
241 if (!flecs_component_ids_get(world, s_index)) {
242 return false;
243 }
244
245 return true;
246 }
247
248 // This function is only used to test cross-translation unit features. No
249 // code other than test cases should invoke this function.
250 static void reset() {
251 s_index = 0;
252 s_size = 0;
253 s_alignment = 0;
254 s_allow_tag = true;
255 }
256
257 static int32_t s_index;
258 static size_t s_size;
259 static size_t s_alignment;
260 static bool s_allow_tag;
261};
262
263// Global templated variables that hold component identifier and other info
264template <typename T> int32_t type_impl<T>::s_index;
265template <typename T> size_t type_impl<T>::s_size;
266template <typename T> size_t type_impl<T>::s_alignment;
267template <typename T> bool type_impl<T>::s_allow_tag( true );
268
269// Front facing class for implicitly registering a component & obtaining
270// static component data
271
272// Regular type
273template <typename T>
274struct type<T, if_not_t< is_pair<T>::value >>
275 : type_impl<base_type_t<T>> { };
276
277// Pair type
278template <typename T>
279struct type<T, if_t< is_pair<T>::value >>
280{
281 // Override id method to return id of pair
282 static id_t id(world_t *world = nullptr) {
283 return ecs_pair(
284 type< pair_first_t<T> >::id(world),
285 type< pair_second_t<T> >::id(world));
286 }
287};
288
289} // namespace _
290
297 using entity::entity;
298
299 untyped_component() : entity() { }
300 explicit untyped_component(flecs::world_t *world, flecs::entity_t id) : entity(world, id) { }
301 explicit untyped_component(flecs::entity_t id) : entity(id) { }
302
303 explicit untyped_component(flecs::world_t *world, const char *name)
304 {
305 world_ = world;
306
307 ecs_entity_desc_t desc = {};
308 desc.name = name;
309 desc.sep = "::";
310 desc.root_sep = "::";
311 desc.use_low_id = true;
312 id_ = ecs_entity_init(world, &desc);
313 }
314
315 explicit untyped_component(world_t *world, const char *name, const char *sep, const char *root_sep)
316 {
317 world_ = world;
318
319 ecs_entity_desc_t desc = {};
320 desc.name = name;
321 desc.sep = sep;
322 desc.root_sep = root_sep;
323 desc.use_low_id = true;
324 id_ = ecs_entity_init(world, &desc);
325 }
326
327protected:
328
329flecs::type_hooks_t get_hooks() const {
330 const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_);
331 if (h) {
332 return *h;
333 } else {
334 return {};
335 }
336}
337
338void set_hooks(flecs::type_hooks_t &h) {
339 h.flags &= ECS_TYPE_HOOKS_ILLEGAL;
340 ecs_set_hooks_id(world_, id_, &h);
341}
342
343public:
344
346 ecs_cmp_t compare_callback)
347{
348 ecs_assert(compare_callback, ECS_INVALID_PARAMETER, NULL);
349 flecs::type_hooks_t h = get_hooks();
350 h.cmp = compare_callback;
351 h.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL;
352 if(h.flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) {
353 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
354 h.equals = NULL;
355 }
356 set_hooks(h);
357 return *this;
358}
359
361 ecs_equals_t equals_callback)
362{
363 ecs_assert(equals_callback, ECS_INVALID_PARAMETER, NULL);
364 flecs::type_hooks_t h = get_hooks();
365 h.equals = equals_callback;
366 h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL;
367 set_hooks(h);
368 return *this;
369}
370
371# ifdef FLECS_META
373# endif
374# ifdef FLECS_METRICS
375# include "mixins/metrics/untyped_component.inl"
376# endif
377};
378
384template <typename T>
396 flecs::world_t *world,
397 const char *name = nullptr,
398 bool allow_tag = true,
399 flecs::id_t id = 0)
400 {
401 world_ = world;
402 id_ = _::type<T>::register_id(world, name, allow_tag, true, true, id);
403 }
404
406 template <typename Func>
407 component<T>& on_add(Func&& func) {
408 using Delegate = typename _::each_delegate<typename std::decay<Func>::type, T>;
409 flecs::type_hooks_t h = get_hooks();
410 ecs_assert(h.on_add == nullptr, ECS_INVALID_OPERATION,
411 "on_add hook is already set");
412 BindingCtx *ctx = get_binding_ctx(h);
413 h.on_add = Delegate::run_add;
414 ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func));
415 ctx->free_on_add = _::free_obj<Delegate>;
416 set_hooks(h);
417 return *this;
418 }
419
421 template <typename Func>
422 component<T>& on_remove(Func&& func) {
423 using Delegate = typename _::each_delegate<
424 typename std::decay<Func>::type, T>;
425 flecs::type_hooks_t h = get_hooks();
426 ecs_assert(h.on_remove == nullptr, ECS_INVALID_OPERATION,
427 "on_remove hook is already set");
428 BindingCtx *ctx = get_binding_ctx(h);
429 h.on_remove = Delegate::run_remove;
430 ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func));
431 ctx->free_on_remove = _::free_obj<Delegate>;
432 set_hooks(h);
433 return *this;
434 }
435
437 template <typename Func>
438 component<T>& on_set(Func&& func) {
439 using Delegate = typename _::each_delegate<
440 typename std::decay<Func>::type, T>;
441 flecs::type_hooks_t h = get_hooks();
442 ecs_assert(h.on_set == nullptr, ECS_INVALID_OPERATION,
443 "on_set hook is already set");
444 BindingCtx *ctx = get_binding_ctx(h);
445 h.on_set = Delegate::run_set;
446 ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func));
447 ctx->free_on_set = _::free_obj<Delegate>;
448 set_hooks(h);
449 return *this;
450 }
451
453 using untyped_component::on_compare;
454 component<T>& on_compare() {
455 ecs_cmp_t handler = _::compare<T>();
456 ecs_assert(handler != NULL, ECS_INVALID_OPERATION,
457 "Type does not have operator> or operator< const or is inaccessible");
458 on_compare(handler);
459 return *this;
460 }
461
463 using cmp_hook = int(*)(const T* a, const T* b, const ecs_type_info_t *ti);
464 component<T>& on_compare(cmp_hook callback) {
465 on_compare(reinterpret_cast<ecs_cmp_t>(callback));
466 return *this;
467 }
468
470 using untyped_component::on_equals;
471 component<T>& on_equals() {
472 ecs_equals_t handler = _::equals<T>();
473 ecs_assert(handler != NULL, ECS_INVALID_OPERATION,
474 "Type does not have operator== const or is inaccessible");
475 on_equals(handler);
476 return *this;
477 }
478
480 using equals_hook = bool(*)(const T* a, const T* b, const ecs_type_info_t *ti);
481 component<T>& on_equals(equals_hook callback) {
482 on_equals(reinterpret_cast<ecs_equals_t>(callback));
483 return *this;
484 }
485
486# ifdef FLECS_META
487# include "mixins/meta/component.inl"
488# endif
489
490private:
491 using BindingCtx = _::component_binding_ctx;
492
493 BindingCtx* get_binding_ctx(flecs::type_hooks_t& h){
494 BindingCtx *result = static_cast<BindingCtx*>(h.binding_ctx);
495 if (!result) {
496 result = FLECS_NEW(BindingCtx);
497 h.binding_ctx = result;
498 h.binding_ctx_free = _::free_obj<BindingCtx>;
499 }
500 return result;
501 }
502};
503
504}
505
void ecs_add_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t id)
Add a (component) id to an entity.
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:368
void ecs_set_hooks_id(ecs_world_t *world, ecs_entity_t id, const ecs_type_hooks_t *hooks)
Register hooks for component.
const ecs_type_hooks_t * ecs_get_hooks_id(const ecs_world_t *world, ecs_entity_t id)
Get 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:92
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:88
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:658
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:664
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:1012
const char * sep
Optional custom separator for hierarchical names.
Definition flecs.h:1024
const char * root_sep
Optional, used for identifiers relative to root.
Definition flecs.h:1028
const char * name
Name of the entity.
Definition flecs.h:1019
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:1040
ecs_iter_action_t on_remove
Callback that is invoked when an instance of the component is removed.
Definition flecs.h:974
void * binding_ctx
Language binding context.
Definition flecs.h:977
ecs_flags32_t flags
Hook flags.
Definition flecs.h:959
ecs_cmp_t cmp
Compare hook.
Definition flecs.h:950
ecs_iter_action_t on_set
Callback that is invoked when an instance of the component is set.
Definition flecs.h:969
ecs_xtor_t ctor
ctor
Definition flecs.h:926
ecs_iter_action_t on_add
Callback that is invoked when an instance of a component is added.
Definition flecs.h:964
ecs_ctx_free_t binding_ctx_free
Callback to free binding_ctx.
Definition flecs.h:981
ecs_equals_t equals
Equals hook.
Definition flecs.h:953
Type that contains component information (passed to ctors/dtors/...)
Definition flecs.h:989
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.
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:137