From bb6304ecac5da6ca08412093eb9cb0916b9d647e Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Wed, 18 Sep 2024 16:51:00 +0200 Subject: [PATCH] mixin: basic_reactive_mixin draft --- src/entt/entity/mixin.hpp | 184 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/src/entt/entity/mixin.hpp b/src/entt/entity/mixin.hpp index b498f4037..6cc3c7e79 100644 --- a/src/entt/entity/mixin.hpp +++ b/src/entt/entity/mixin.hpp @@ -5,6 +5,7 @@ #include #include "../config/config.h" #include "../core/any.hpp" +#include "../core/type_info.hpp" #include "../signal/sigh.hpp" #include "entity.hpp" #include "fwd.hpp" @@ -344,6 +345,189 @@ private: sigh_type update; }; +/** + * @brief Mixin type used to add _reactive_ support to storage types. + * @tparam Type Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_reactive_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using basic_registry_type = basic_registry; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + [[nodiscard]] auto &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + + void emplace_element(const Registry &, typename underlying_type::entity_type entity) { + if(!underlying_type::contains(entity)) { + underlying_type::emplace(entity); + } + } + +private: + void bind_any(any value) noexcept final { + auto *reg = any_cast(&value); + owner = reg ? reg : owner; + underlying_type::bind_any(std::move(value)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_reactive_mixin() + : basic_reactive_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_reactive_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{} { + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_reactive_mixin(const basic_reactive_mixin &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_reactive_mixin(basic_reactive_mixin &&other) noexcept + : underlying_type{std::move(other)}, + owner{other.owner} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator) + : underlying_type{std::move(other), allocator}, + owner{other.owner} {} + + /*! @brief Default destructor. */ + ~basic_reactive_mixin() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This mixin. + */ + basic_reactive_mixin &operator=(const basic_reactive_mixin &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This mixin. + */ + basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept { + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_reactive_mixin &other) noexcept { + using std::swap; + swap(owner, other.owner); + underlying_type::swap(other); + } + + /** + * @brief Makes storage _react_ to creation of objects of the given type. + * @tparam Type Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + */ + template + void on_construct(const id_type id = type_hash::value()) { + owner_or_assert().storage(id).on_construct(*this); + } + + /** + * @brief Makes storage _react_ to update of objects of the given type. + * @tparam Type Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + */ + template + void on_update(const id_type id = type_hash::value()) { + owner_or_assert().storage(id).on_update(*this); + } + + /** + * @brief Makes storage _react_ to destruction of objects of the given type. + * @tparam Type Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + */ + template + void on_destroy(const id_type id = type_hash::value()) { + owner_or_assert().storage(id).on_destroy(*this); + } + + /** + * @brief Checks if a mixin refers to non-null registry. + * @return True if the mixin refers to non-null registry, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (owner != nullptr); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] const registry_type ®istry() const noexcept { + return owner_or_assert(); + } + + /*! @copydoc registry */ + [[nodiscard]] registry_type ®istry() noexcept { + return owner_or_assert(); + } + + /** + * @brief Returns a view that is filtered by the underlying storage. + * @tparam Type Types of elements used to construct the view. + * @tparam Exclude Types of elements used to filter the view. + * @return A newly created view. + */ + template + [[nodiscard]] basic_view...>, exclude_t...>> + view(exclude_t = exclude_t{}) const { + basic_registry_type &parent = owner_or_assert(); + basic_view...>, exclude_t...>> elem{}; + [&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.storage>()..., parent.storage>()..., this); + return elem; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view...>, exclude_t...>> + view(exclude_t = exclude_t{}) { + basic_registry_type &parent = owner_or_assert(); + return {*this, parent.storage>()..., parent.storage>()...}; + } + +private: + basic_registry_type *owner; +}; + } // namespace entt #endif