EnTT 4.0.0
Loading...
Searching...
No Matches
mixin.hpp
1#ifndef ENTT_ENTITY_MIXIN_HPP
2#define ENTT_ENTITY_MIXIN_HPP
3
4#include "../config/config.h"
5#include "../core/any.hpp"
6#include "../core/type_info.hpp"
7#include "../signal/sigh.hpp"
8#include "../stl/concepts.hpp"
9#include "../stl/iterator.hpp"
10#include "../stl/type_traits.hpp"
11#include "../stl/utility.hpp"
12#include "../stl/vector.hpp"
13#include "entity.hpp"
14#include "fwd.hpp"
15
16namespace entt {
17
19namespace internal {
20
21template<typename, typename>
22struct has_on_construct final: stl::false_type {};
23
24template<typename Type, typename Registry>
25requires stl::invocable<decltype(&Type::on_construct), Registry &, typename Registry::entity_type>
26struct has_on_construct<Type, Registry>: stl::true_type {};
27
28template<typename, typename>
29struct has_on_update final: stl::false_type {};
30
31template<typename Type, typename Registry>
32requires stl::invocable<decltype(&Type::on_update), Registry &, typename Registry::entity_type>
33struct has_on_update<Type, Registry>: stl::true_type {};
34
35template<typename, typename>
36struct has_on_destroy final: stl::false_type {};
37
38template<typename Type, typename Registry>
39requires stl::invocable<decltype(&Type::on_destroy), Registry &, typename Registry::entity_type>
40struct has_on_destroy<Type, Registry>: stl::true_type {};
41
42} // namespace internal
44
59template<typename Type, typename Registry>
60class basic_sigh_mixin final: public Type {
61 using underlying_type = Type;
62 using owner_type = Registry;
63
65 using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
66 using underlying_iterator = underlying_type::base_type::basic_iterator;
67
68 static_assert(stl::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
69
70 [[nodiscard]] auto &owner_or_assert() const noexcept {
71 ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
72 return static_cast<owner_type &>(*owner);
73 }
74
75private:
76 void pop(underlying_iterator first, underlying_iterator last) final {
77 if(auto &reg = owner_or_assert(); destruction.empty()) {
78 underlying_type::pop(first, last);
79 } else {
80 for(; first != last; ++first) {
81 const auto entt = *first;
82 destruction.publish(reg, entt);
83 const auto it = underlying_type::find(entt);
84 underlying_type::pop(it, it + 1u);
85 }
86 }
87 }
88
89 void pop_all() final {
90 if(auto &reg = owner_or_assert(); !destruction.empty()) {
91 if constexpr(stl::is_same_v<typename underlying_type::element_type, entity_type>) {
92 for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) {
93 destruction.publish(reg, underlying_type::base_type::operator[](pos));
94 }
95 } else {
96 for(auto entt: static_cast<underlying_type::base_type &>(*this)) {
97 if constexpr(underlying_type::storage_policy == deletion_policy::in_place) {
98 if(entt != tombstone) {
99 destruction.publish(reg, entt);
100 }
101 } else {
102 destruction.publish(reg, entt);
103 }
104 }
105 }
106 }
107
108 underlying_type::pop_all();
109 }
110
111 underlying_iterator try_emplace(const underlying_type::entity_type entt, const bool force_back, const void *value) final {
112 const auto it = underlying_type::try_emplace(entt, force_back, value);
113
114 if(auto &reg = owner_or_assert(); it != underlying_type::base_type::end()) {
115 construction.publish(reg, *it);
116 }
117
118 return it;
119 }
120
121 void bind_any(any value) noexcept final {
122 owner = any_cast<basic_registry_type>(&value);
123
124 if constexpr(!stl::is_same_v<registry_type, basic_registry_type>) {
125 if(owner == nullptr) {
126 owner = any_cast<registry_type>(&value);
127 }
128 }
129
130 underlying_type::bind_any(stl::move(value));
131 }
132
133public:
135 using allocator_type = underlying_type::allocator_type;
137 using entity_type = underlying_type::entity_type;
139 using registry_type = owner_type;
140
144
149 explicit basic_sigh_mixin(const allocator_type &allocator)
150 : underlying_type{allocator},
151 owner{},
152 construction{allocator},
153 destruction{allocator},
154 update{allocator} {
155 if constexpr(internal::has_on_construct<typename underlying_type::element_type, Registry>::value) {
156 sink{construction}.template connect<&underlying_type::element_type::on_construct>();
157 }
158
159 if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
160 sink{update}.template connect<&underlying_type::element_type::on_update>();
161 }
162
163 if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::value) {
164 sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
165 }
166 }
167
170
176 : underlying_type{static_cast<underlying_type &&>(other)},
177 owner{other.owner},
178 construction{stl::move(other.construction)},
179 destruction{stl::move(other.destruction)},
180 update{stl::move(other.update)} {}
181
188 : underlying_type{static_cast<underlying_type &&>(other), allocator},
189 owner{other.owner},
190 construction{stl::move(other.construction), allocator},
191 destruction{stl::move(other.destruction), allocator},
192 update{stl::move(other.update), allocator} {}
193
195 ~basic_sigh_mixin() override = default;
196
202
209 swap(other);
210 return *this;
211 }
212
217 void swap(basic_sigh_mixin &other) noexcept {
218 using stl::swap;
219 swap(owner, other.owner);
220 swap(construction, other.construction);
221 swap(destruction, other.destruction);
222 swap(update, other.update);
223 underlying_type::swap(other);
224 }
225
237 [[nodiscard]] auto on_construct() noexcept {
238 return sink{construction};
239 }
240
252 [[nodiscard]] auto on_update() noexcept {
253 return sink{update};
254 }
255
267 [[nodiscard]] auto on_destroy() noexcept {
268 return sink{destruction};
269 }
270
275 [[nodiscard]] explicit operator bool() const noexcept {
276 return (owner != nullptr);
277 }
278
283 [[nodiscard]] const registry_type &registry() const noexcept {
284 return owner_or_assert();
285 }
286
288 [[nodiscard]] registry_type &registry() noexcept {
289 return owner_or_assert();
290 }
291
296 auto generate() {
297 const auto entt = underlying_type::generate();
298 construction.publish(owner_or_assert(), entt);
299 return entt;
300 }
301
308 const auto entt = underlying_type::generate(hint);
309 construction.publish(owner_or_assert(), entt);
310 return entt;
311 }
312
319 template<stl::output_iterator<entity_type> It>
320 void generate(It first, It last) {
321 underlying_type::generate(first, last);
322
323 if(auto &reg = owner_or_assert(); !construction.empty()) {
324 for(; first != last; ++first) {
325 construction.publish(reg, *first);
326 }
327 }
328 }
329
337 template<typename... Args>
338 decltype(auto) emplace(const entity_type entt, Args &&...args) {
339 underlying_type::emplace(entt, stl::forward<Args>(args)...);
340 construction.publish(owner_or_assert(), entt);
341 return this->get(entt);
342 }
343
351 template<typename... Func>
352 decltype(auto) patch(const entity_type entt, Func &&...func) {
353 underlying_type::patch(entt, stl::forward<Func>(func)...);
354 update.publish(owner_or_assert(), entt);
355 return this->get(entt);
356 }
357
366 template<typename... Args>
367 void insert(stl::input_iterator auto first, stl::input_iterator auto last, Args &&...args) {
368 auto from = underlying_type::size();
369 underlying_type::insert(first, last, stl::forward<Args>(args)...);
370
371 if(auto &reg = owner_or_assert(); !construction.empty()) {
372 // fine as long as insert passes force_back true to try_emplace
373 for(const auto to = underlying_type::size(); from != to; ++from) {
374 construction.publish(reg, underlying_type::operator[](from));
375 }
376 }
377 }
378
379private:
380 basic_registry_type *owner;
381 sigh_type construction;
382 sigh_type destruction;
383 sigh_type update;
384};
385
391template<typename Type, typename Registry>
392class basic_reactive_mixin final: public Type {
393 using underlying_type = Type;
394 using owner_type = Registry;
395
396 using alloc_traits = stl::allocator_traits<typename underlying_type::allocator_type>;
398 using container_type = stl::vector<connection, typename alloc_traits::template rebind_alloc<connection>>;
399
400 static_assert(stl::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
401
402 [[nodiscard]] auto &owner_or_assert() const noexcept {
403 ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
404 return static_cast<owner_type &>(*owner);
405 }
406
407 void emplace_element(const Registry &, underlying_type::entity_type entity) {
408 if(!underlying_type::contains(entity)) {
409 underlying_type::emplace(entity);
410 }
411 }
412
413private:
414 void bind_any(any value) noexcept final {
415 owner = any_cast<basic_registry_type>(&value);
416
417 if constexpr(!stl::is_same_v<registry_type, basic_registry_type>) {
418 if(owner == nullptr) {
419 owner = any_cast<registry_type>(&value);
420 }
421 }
422
423 underlying_type::bind_any(stl::move(value));
424 }
425
426public:
428 using allocator_type = underlying_type::allocator_type;
430 using entity_type = underlying_type::entity_type;
432 using registry_type = owner_type;
433
437
442 explicit basic_reactive_mixin(const allocator_type &allocator)
443 : underlying_type{allocator},
444 owner{},
445 conn{allocator} {
446 }
447
450
456 : underlying_type{static_cast<underlying_type &&>(other)},
457 owner{other.owner},
458 conn{stl::move(other.conn)} {
459 }
460
467 : underlying_type{static_cast<underlying_type &&>(other), allocator},
468 owner{other.owner},
469 conn{stl::move(other.conn), allocator} {
470 }
471
473 ~basic_reactive_mixin() override = default;
474
480
487 underlying_type::swap(other);
488 return *this;
489 }
490
498 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
500 auto curr = owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
501 conn.push_back(stl::move(curr));
502 return *this;
503 }
504
512 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
514 auto curr = owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
515 conn.push_back(stl::move(curr));
516 return *this;
517 }
518
526 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
528 auto curr = owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
529 conn.push_back(stl::move(curr));
530 return *this;
531 }
532
537 [[nodiscard]] explicit operator bool() const noexcept {
538 return (owner != nullptr);
539 }
540
545 [[nodiscard]] const registry_type &registry() const noexcept {
546 return owner_or_assert();
547 }
548
550 [[nodiscard]] registry_type &registry() noexcept {
551 return owner_or_assert();
552 }
553
560 template<typename... Get, typename... Exclude>
563 const owner_type &parent = owner_or_assert();
565 [&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage<stl::remove_const_t<Exclude>>()..., parent.template storage<stl::remove_const_t<Get>>()..., this);
566 return elem;
567 }
568
570 template<typename... Get, typename... Exclude>
571 [[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<Exclude>...>>
573 stl::conditional_t<((stl::is_const_v<Get> && ...) && (stl::is_const_v<Exclude> && ...)), const owner_type, owner_type> &parent = owner_or_assert();
574 return {*this, parent.template storage<stl::remove_const_t<Get>>()..., parent.template storage<stl::remove_const_t<Exclude>>()...};
575 }
576
578 void reset() {
579 for(auto &&curr: conn) {
580 curr.release();
581 }
582
583 conn.clear();
584 }
585
586private:
587 basic_registry_type *owner;
588 container_type conn;
589};
590
591} // namespace entt
592
593#endif
basic_view< get_t< const basic_reactive_mixin, typename basic_registry_type::template storage_for_type< Get >... >, exclude_t< typename basic_registry_type::template storage_for_type< Exclude >... > > view(exclude_t< Exclude... >=exclude_t{})
Definition mixin.hpp:572
basic_reactive_mixin()
Default constructor.
Definition mixin.hpp:435
basic_view< get_t< const basic_reactive_mixin, typename basic_registry_type::template storage_for_type< const Get >... >, exclude_t< typename basic_registry_type::template storage_for_type< const Exclude >... > > view(exclude_t< Exclude... >=exclude_t{}) const
Definition mixin.hpp:562
Fast and reliable entity-component system.
Definition registry.hpp:211
void swap(basic_sigh_mixin &other) noexcept
Exchanges the contents with those of a given storage.
Definition mixin.hpp:217
basic_sigh_mixin()
Default constructor.
Definition mixin.hpp:142
void insert(stl::input_iterator auto first, stl::input_iterator auto last, Args &&...args)
Definition mixin.hpp:367
View implementation.
Definition fwd.hpp:48
Unmanaged signal handler.
Definition fwd.hpp:25
Sink class.
Definition fwd.hpp:22
Custom EnTT namespace for the standard template library.
Definition entt.hpp:5
EnTT default namespace.
Definition dense_map.hpp:25
basic_view(Type &...storage) -> basic_view< get_t< Type... >, exclude_t<> >
Deduction guide.
entity
Default entity identifier.
Definition fwd.hpp:15
@ in_place
In-place deletion policy.
Definition fwd.hpp:22
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition entity.hpp:308
basic_any<> any
Alias declaration for the most common use case.
Definition fwd.hpp:32
constexpr get_t< Type... > get
Variable template for lists of observed elements.
Definition fwd.hpp:168
stl::remove_const_t< Type > any_cast(const basic_any< Len, Align > &data) noexcept
Performs type-safe access to the contained object.
Definition any.hpp:545
stl::uint32_t id_type
Alias declaration for type identifiers.
Definition fwd.hpp:29
basic_storage< Type > storage
Alias declaration for the most common use case.
Definition fwd.hpp:79
Alias for exclusion lists.
Definition fwd.hpp:141
static constexpr id_type value() noexcept
Returns the numeric representation of a given type.