EnTT 3.16.0
Loading...
Searching...
No Matches
mixin.hpp
1#ifndef ENTT_ENTITY_MIXIN_HPP
2#define ENTT_ENTITY_MIXIN_HPP
3
4#include <type_traits>
5#include <utility>
6#include "../config/config.h"
7#include "../core/any.hpp"
8#include "../core/type_info.hpp"
9#include "../signal/sigh.hpp"
10#include "entity.hpp"
11#include "fwd.hpp"
12
13namespace entt {
14
16namespace internal {
17
18template<typename, typename, typename = void>
19struct has_on_construct final: std::false_type {};
20
21template<typename Type, typename Registry>
22struct has_on_construct<Type, Registry, std::void_t<decltype(Type::on_construct(std::declval<Registry &>(), std::declval<Registry>().create()))>>
23 : std::true_type {};
24
25template<typename, typename, typename = void>
26struct has_on_update final: std::false_type {};
27
28template<typename Type, typename Registry>
29struct has_on_update<Type, Registry, std::void_t<decltype(Type::on_update(std::declval<Registry &>(), std::declval<Registry>().create()))>>
30 : std::true_type {};
31
32template<typename, typename, typename = void>
33struct has_on_destroy final: std::false_type {};
34
35template<typename Type, typename Registry>
36struct has_on_destroy<Type, Registry, std::void_t<decltype(Type::on_destroy(std::declval<Registry &>(), std::declval<Registry>().create()))>>
37 : std::true_type {};
38
39} // namespace internal
41
56template<typename Type, typename Registry>
57class basic_sigh_mixin final: public Type {
58 using underlying_type = Type;
59 using owner_type = Registry;
60
62 using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
63 using underlying_iterator = typename underlying_type::base_type::basic_iterator;
64
65 static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
66
67 [[nodiscard]] auto &owner_or_assert() const noexcept {
68 ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
69 return static_cast<owner_type &>(*owner);
70 }
71
72private:
73 void pop(underlying_iterator first, underlying_iterator last) final {
74 if(auto &reg = owner_or_assert(); destruction.empty()) {
75 underlying_type::pop(first, last);
76 } else {
77 for(; first != last; ++first) {
78 const auto entt = *first;
79 destruction.publish(reg, entt);
80 const auto it = underlying_type::find(entt);
81 underlying_type::pop(it, it + 1u);
82 }
83 }
84 }
85
86 void pop_all() final {
87 if(auto &reg = owner_or_assert(); !destruction.empty()) {
88 if constexpr(std::is_same_v<typename underlying_type::element_type, entity_type>) {
89 for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) {
90 destruction.publish(reg, underlying_type::base_type::operator[](pos));
91 }
92 } else {
93 for(auto entt: static_cast<typename underlying_type::base_type &>(*this)) {
94 if constexpr(underlying_type::storage_policy == deletion_policy::in_place) {
95 if(entt != tombstone) {
96 destruction.publish(reg, entt);
97 }
98 } else {
99 destruction.publish(reg, entt);
100 }
101 }
102 }
103 }
104
105 underlying_type::pop_all();
106 }
107
108 underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final {
109 const auto it = underlying_type::try_emplace(entt, force_back, value);
110
111 if(auto &reg = owner_or_assert(); it != underlying_type::base_type::end()) {
112 construction.publish(reg, *it);
113 }
114
115 return it;
116 }
117
118 void bind_any(any value) noexcept final {
119 owner = any_cast<basic_registry_type>(&value);
120
121 if constexpr(!std::is_same_v<registry_type, basic_registry_type>) {
122 if(owner == nullptr) {
123 owner = any_cast<registry_type>(&value);
124 }
125 }
126
127 underlying_type::bind_any(std::move(value));
128 }
129
130public:
132 using allocator_type = typename underlying_type::allocator_type;
134 using entity_type = typename underlying_type::entity_type;
136 using registry_type = owner_type;
137
141
146 explicit basic_sigh_mixin(const allocator_type &allocator)
147 : underlying_type{allocator},
148 owner{},
149 construction{allocator},
150 destruction{allocator},
151 update{allocator} {
152 if constexpr(internal::has_on_construct<typename underlying_type::element_type, Registry>::value) {
153 sink{construction}.template connect<&underlying_type::element_type::on_construct>();
154 }
155
156 if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
157 sink{update}.template connect<&underlying_type::element_type::on_update>();
158 }
159
160 if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::value) {
161 sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
162 }
163 }
164
167
173 : underlying_type{static_cast<underlying_type &&>(other)},
174 owner{other.owner},
175 construction{std::move(other.construction)},
176 destruction{std::move(other.destruction)},
177 update{std::move(other.update)} {}
178
185 : underlying_type{static_cast<underlying_type &&>(other), allocator},
186 owner{other.owner},
187 construction{std::move(other.construction), allocator},
188 destruction{std::move(other.destruction), allocator},
189 update{std::move(other.update), allocator} {}
190
192 ~basic_sigh_mixin() override = default;
193
199
206 swap(other);
207 return *this;
208 }
209
214 void swap(basic_sigh_mixin &other) noexcept {
215 using std::swap;
216 swap(owner, other.owner);
217 swap(construction, other.construction);
218 swap(destruction, other.destruction);
219 swap(update, other.update);
220 underlying_type::swap(other);
221 }
222
234 [[nodiscard]] auto on_construct() noexcept {
235 return sink{construction};
236 }
237
249 [[nodiscard]] auto on_update() noexcept {
250 return sink{update};
251 }
252
264 [[nodiscard]] auto on_destroy() noexcept {
265 return sink{destruction};
266 }
267
272 [[nodiscard]] explicit operator bool() const noexcept {
273 return (owner != nullptr);
274 }
275
280 [[nodiscard]] const registry_type &registry() const noexcept {
281 return owner_or_assert();
282 }
283
285 [[nodiscard]] registry_type &registry() noexcept {
286 return owner_or_assert();
287 }
288
293 auto generate() {
294 const auto entt = underlying_type::generate();
295 construction.publish(owner_or_assert(), entt);
296 return entt;
297 }
298
305 const auto entt = underlying_type::generate(hint);
306 construction.publish(owner_or_assert(), entt);
307 return entt;
308 }
309
316 template<typename It>
317 void generate(It first, It last) {
318 underlying_type::generate(first, last);
319
320 if(auto &reg = owner_or_assert(); !construction.empty()) {
321 for(; first != last; ++first) {
322 construction.publish(reg, *first);
323 }
324 }
325 }
326
334 template<typename... Args>
335 decltype(auto) emplace(const entity_type entt, Args &&...args) {
336 underlying_type::emplace(entt, std::forward<Args>(args)...);
337 construction.publish(owner_or_assert(), entt);
338 return this->get(entt);
339 }
340
348 template<typename... Func>
349 decltype(auto) patch(const entity_type entt, Func &&...func) {
350 underlying_type::patch(entt, std::forward<Func>(func)...);
351 update.publish(owner_or_assert(), entt);
352 return this->get(entt);
353 }
354
364 template<typename It, typename... Args>
365 void insert(It first, It last, Args &&...args) {
366 auto from = underlying_type::size();
367 underlying_type::insert(first, last, std::forward<Args>(args)...);
368
369 if(auto &reg = owner_or_assert(); !construction.empty()) {
370 // fine as long as insert passes force_back true to try_emplace
371 for(const auto to = underlying_type::size(); from != to; ++from) {
372 construction.publish(reg, underlying_type::operator[](from));
373 }
374 }
375 }
376
377private:
378 basic_registry_type *owner;
379 sigh_type construction;
380 sigh_type destruction;
381 sigh_type update;
382};
383
389template<typename Type, typename Registry>
390class basic_reactive_mixin final: public Type {
391 using underlying_type = Type;
392 using owner_type = Registry;
393
394 using alloc_traits = std::allocator_traits<typename underlying_type::allocator_type>;
396 using container_type = std::vector<connection, typename alloc_traits::template rebind_alloc<connection>>;
397
398 static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
399
400 [[nodiscard]] auto &owner_or_assert() const noexcept {
401 ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
402 return static_cast<owner_type &>(*owner);
403 }
404
405 void emplace_element(const Registry &, typename underlying_type::entity_type entity) {
406 if(!underlying_type::contains(entity)) {
407 underlying_type::emplace(entity);
408 }
409 }
410
411private:
412 void bind_any(any value) noexcept final {
413 owner = any_cast<basic_registry_type>(&value);
414
415 if constexpr(!std::is_same_v<registry_type, basic_registry_type>) {
416 if(owner == nullptr) {
417 owner = any_cast<registry_type>(&value);
418 }
419 }
420
421 underlying_type::bind_any(std::move(value));
422 }
423
424public:
426 using allocator_type = typename underlying_type::allocator_type;
428 using entity_type = typename underlying_type::entity_type;
430 using registry_type = owner_type;
431
435
440 explicit basic_reactive_mixin(const allocator_type &allocator)
441 : underlying_type{allocator},
442 owner{},
443 conn{allocator} {
444 }
445
448
454 : underlying_type{static_cast<underlying_type &&>(other)},
455 owner{other.owner},
456 conn{std::move(other.conn)} {
457 }
458
465 : underlying_type{static_cast<underlying_type &&>(other), allocator},
466 owner{other.owner},
467 conn{std::move(other.conn), allocator} {
468 }
469
471 ~basic_reactive_mixin() override = default;
472
478
485 underlying_type::swap(other);
486 return *this;
487 }
488
496 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
498 auto curr = owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
499 conn.push_back(std::move(curr));
500 return *this;
501 }
502
510 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
512 auto curr = owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
513 conn.push_back(std::move(curr));
514 return *this;
515 }
516
524 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
526 auto curr = owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
527 conn.push_back(std::move(curr));
528 return *this;
529 }
530
535 [[nodiscard]] explicit operator bool() const noexcept {
536 return (owner != nullptr);
537 }
538
543 [[nodiscard]] const registry_type &registry() const noexcept {
544 return owner_or_assert();
545 }
546
548 [[nodiscard]] registry_type &registry() noexcept {
549 return owner_or_assert();
550 }
551
558 template<typename... Get, typename... Exclude>
561 const owner_type &parent = owner_or_assert();
563 [&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage<std::remove_const_t<Exclude>>()..., parent.template storage<std::remove_const_t<Get>>()..., this);
564 return elem;
565 }
566
568 template<typename... Get, typename... Exclude>
569 [[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>...>>
571 std::conditional_t<((std::is_const_v<Get> && ...) && (std::is_const_v<Exclude> && ...)), const owner_type, owner_type> &parent = owner_or_assert();
572 return {*this, parent.template storage<std::remove_const_t<Get>>()..., parent.template storage<std::remove_const_t<Exclude>>()...};
573 }
574
576 void reset() {
577 for(auto &&curr: conn) {
578 curr.release();
579 }
580
581 conn.clear();
582 }
583
584private:
585 basic_registry_type *owner;
586 container_type conn;
587};
588
589} // namespace entt
590
591#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:570
basic_reactive_mixin()
Default constructor.
Definition mixin.hpp:433
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:560
Fast and reliable entity-component system.
Definition registry.hpp:238
void swap(basic_sigh_mixin &other) noexcept
Exchanges the contents with those of a given storage.
Definition mixin.hpp:214
basic_sigh_mixin()
Default constructor.
Definition mixin.hpp:139
View implementation.
Definition fwd.hpp:47
Unmanaged signal handler.
Definition fwd.hpp:25
Sink class.
Definition fwd.hpp:22
EnTT default namespace.
Definition dense_map.hpp:22
basic_view(Type &...storage) -> basic_view< get_t< Type... >, exclude_t<> >
Deduction guide.
entity
Default entity identifier.
Definition fwd.hpp:14
std::remove_const_t< Type > any_cast(const basic_any< Len, Align > &data) noexcept
Performs type-safe access to the contained object.
Definition any.hpp:547
std::uint32_t id_type
Alias declaration for type identifiers.
Definition fwd.hpp:29
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition entity.hpp:384
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:167
@ in_place
In-place deletion policy.
Definition fwd.hpp:21
basic_storage< Type > storage
Alias declaration for the most common use case.
Definition fwd.hpp:78
Alias for exclusion lists.
Definition fwd.hpp:140
static constexpr id_type value() noexcept
Returns the numeric representation of a given type.