EnTT 3.15.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 entt::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 entt::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 entt::sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
162 }
163 }
164
167
172 // NOLINTBEGIN(bugprone-use-after-move)
174 : underlying_type{std::move(other)},
175 owner{other.owner},
176 construction{std::move(other.construction)},
177 destruction{std::move(other.destruction)},
178 update{std::move(other.update)} {}
179 // NOLINTEND(bugprone-use-after-move)
180
186 // NOLINTBEGIN(bugprone-use-after-move)
188 : underlying_type{std::move(other), allocator},
189 owner{other.owner},
190 construction{std::move(other.construction), allocator},
191 destruction{std::move(other.destruction), allocator},
192 update{std::move(other.update), allocator} {}
193 // NOLINTEND(bugprone-use-after-move)
194
196 ~basic_sigh_mixin() override = default;
197
203
210 swap(other);
211 return *this;
212 }
213
218 void swap(basic_sigh_mixin &other) noexcept {
219 using std::swap;
220 swap(owner, other.owner);
221 swap(construction, other.construction);
222 swap(destruction, other.destruction);
223 swap(update, other.update);
224 underlying_type::swap(other);
225 }
226
238 [[nodiscard]] auto on_construct() noexcept {
239 return sink{construction};
240 }
241
253 [[nodiscard]] auto on_update() noexcept {
254 return sink{update};
255 }
256
268 [[nodiscard]] auto on_destroy() noexcept {
269 return sink{destruction};
270 }
271
276 [[nodiscard]] explicit operator bool() const noexcept {
277 return (owner != nullptr);
278 }
279
284 [[nodiscard]] const registry_type &registry() const noexcept {
285 return owner_or_assert();
286 }
287
289 [[nodiscard]] registry_type &registry() noexcept {
290 return owner_or_assert();
291 }
292
297 auto generate() {
298 const auto entt = underlying_type::generate();
299 construction.publish(owner_or_assert(), entt);
300 return entt;
301 }
302
309 const auto entt = underlying_type::generate(hint);
310 construction.publish(owner_or_assert(), entt);
311 return entt;
312 }
313
320 template<typename It>
321 void generate(It first, It last) {
322 underlying_type::generate(first, last);
323
324 if(auto &reg = owner_or_assert(); !construction.empty()) {
325 for(; first != last; ++first) {
326 construction.publish(reg, *first);
327 }
328 }
329 }
330
338 template<typename... Args>
339 decltype(auto) emplace(const entity_type entt, Args &&...args) {
340 underlying_type::emplace(entt, std::forward<Args>(args)...);
341 construction.publish(owner_or_assert(), entt);
342 return this->get(entt);
343 }
344
352 template<typename... Func>
353 decltype(auto) patch(const entity_type entt, Func &&...func) {
354 underlying_type::patch(entt, std::forward<Func>(func)...);
355 update.publish(owner_or_assert(), entt);
356 return this->get(entt);
357 }
358
368 template<typename It, typename... Args>
369 void insert(It first, It last, Args &&...args) {
370 auto from = underlying_type::size();
371 underlying_type::insert(first, last, std::forward<Args>(args)...);
372
373 if(auto &reg = owner_or_assert(); !construction.empty()) {
374 // fine as long as insert passes force_back true to try_emplace
375 for(const auto to = underlying_type::size(); from != to; ++from) {
376 construction.publish(reg, underlying_type::operator[](from));
377 }
378 }
379 }
380
381private:
382 basic_registry_type *owner;
383 sigh_type construction;
384 sigh_type destruction;
385 sigh_type update;
386};
387
393template<typename Type, typename Registry>
394class basic_reactive_mixin final: public Type {
395 using underlying_type = Type;
396 using owner_type = Registry;
397
398 using alloc_traits = std::allocator_traits<typename underlying_type::allocator_type>;
400 using container_type = std::vector<connection, typename alloc_traits::template rebind_alloc<connection>>;
401
402 static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
403
404 [[nodiscard]] auto &owner_or_assert() const noexcept {
405 ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
406 return static_cast<owner_type &>(*owner);
407 }
408
409 void emplace_element(const Registry &, typename underlying_type::entity_type entity) {
410 if(!underlying_type::contains(entity)) {
411 underlying_type::emplace(entity);
412 }
413 }
414
415private:
416 void bind_any(any value) noexcept final {
417 owner = any_cast<basic_registry_type>(&value);
418
419 if constexpr(!std::is_same_v<registry_type, basic_registry_type>) {
420 if(owner == nullptr) {
421 owner = any_cast<registry_type>(&value);
422 }
423 }
424
425 underlying_type::bind_any(std::move(value));
426 }
427
428public:
430 using allocator_type = typename underlying_type::allocator_type;
432 using entity_type = typename underlying_type::entity_type;
434 using registry_type = owner_type;
435
439
444 explicit basic_reactive_mixin(const allocator_type &allocator)
445 : underlying_type{allocator},
446 owner{},
447 conn{allocator} {
448 }
449
452
457 // NOLINTBEGIN(bugprone-use-after-move)
459 : underlying_type{std::move(other)},
460 owner{other.owner},
461 conn{} {
462 }
463 // NOLINTEND(bugprone-use-after-move)
464
470 // NOLINTBEGIN(bugprone-use-after-move)
472 : underlying_type{std::move(other), allocator},
473 owner{other.owner},
474 conn{allocator} {
475 }
476 // NOLINTEND(bugprone-use-after-move)
477
479 ~basic_reactive_mixin() override = default;
480
486
493 underlying_type::swap(other);
494 return *this;
495 }
496
504 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
506 auto curr = owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
507 conn.push_back(std::move(curr));
508 return *this;
509 }
510
518 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
520 auto curr = owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
521 conn.push_back(std::move(curr));
522 return *this;
523 }
524
532 template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
534 auto curr = owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
535 conn.push_back(std::move(curr));
536 return *this;
537 }
538
543 [[nodiscard]] explicit operator bool() const noexcept {
544 return (owner != nullptr);
545 }
546
551 [[nodiscard]] const registry_type &registry() const noexcept {
552 return owner_or_assert();
553 }
554
556 [[nodiscard]] registry_type &registry() noexcept {
557 return owner_or_assert();
558 }
559
566 template<typename... Get, typename... Exclude>
569 const owner_type &parent = owner_or_assert();
571 [&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);
572 return elem;
573 }
574
576 template<typename... Get, typename... Exclude>
577 [[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>...>>
579 std::conditional_t<((std::is_const_v<Get> && ...) && (std::is_const_v<Exclude> && ...)), const owner_type, owner_type> &parent = owner_or_assert();
580 return {*this, parent.template storage<std::remove_const_t<Get>>()..., parent.template storage<std::remove_const_t<Exclude>>()...};
581 }
582
584 void reset() {
585 for(auto &&curr: conn) {
586 curr.release();
587 }
588
589 conn.clear();
590 }
591
592private:
593 basic_registry_type *owner;
594 container_type conn;
595};
596
597} // namespace entt
598
599#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:578
basic_reactive_mixin()
Default constructor.
Definition mixin.hpp:437
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:568
Fast and reliable entity-component system.
Definition registry.hpp:234
void swap(basic_sigh_mixin &other) noexcept
Exchanges the contents with those of a given storage.
Definition mixin.hpp:218
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:464
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.