diff --git a/CMakeLists.txt b/CMakeLists.txt index abeb6f3a6..8e9e73d42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,7 +145,6 @@ if(ENTT_INCLUDE_HEADERS) $ $ $ - $ $ $ $ diff --git a/entt.imp b/entt.imp index ddf611eab..94cd6a420 100644 --- a/entt.imp +++ b/entt.imp @@ -20,7 +20,6 @@ { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "", "public" ] }, { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "", "public" ] }, { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "", "public" ] }, - { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "", "public" ] }, { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "", "public" ] }, { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "", "public" ] }, { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "", "public" ] }, diff --git a/src/entt/entity/fwd.hpp b/src/entt/entity/fwd.hpp index 07f2511c5..1f184120c 100644 --- a/src/entt/entity/fwd.hpp +++ b/src/entt/entity/fwd.hpp @@ -47,9 +47,6 @@ class basic_runtime_view; template class basic_group; -template> -class basic_observer; - template class basic_organizer; @@ -92,9 +89,6 @@ using reactive_mixin = basic_reactive_mixin; -/*! @brief Alias declaration for the most common use case. */ -using observer = basic_observer; - /*! @brief Alias declaration for the most common use case. */ using organizer = basic_organizer; diff --git a/src/entt/entity/observer.hpp b/src/entt/entity/observer.hpp deleted file mode 100644 index 33b08dab0..000000000 --- a/src/entt/entity/observer.hpp +++ /dev/null @@ -1,438 +0,0 @@ -#ifndef ENTT_ENTITY_OBSERVER_HPP -#define ENTT_ENTITY_OBSERVER_HPP - -#include -#include -#include -#include -#include -#include "../core/type_traits.hpp" -#include "fwd.hpp" -#include "storage.hpp" - -namespace entt { - -/*! @brief Grouping matcher. */ -template -struct matcher {}; - -/** - * @brief Collector. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error, but for a few reasonable cases. - */ -template -struct basic_collector; - -/** - * @brief Collector. - * - * A collector contains a set of rules (literally, matchers) to use to track - * entities.
- * Its main purpose is to generate a descriptor that allows an observer to know - * how to connect to a registry. - */ -template<> -struct basic_collector<> { - /** - * @brief Adds a grouping matcher to the collector. - * @tparam AllOf Types of elements tracked by the matcher. - * @tparam NoneOf Types of elements used to filter out entities. - * @return The updated collector. - */ - template - static constexpr auto group(exclude_t = exclude_t{}) noexcept { - return basic_collector, type_list<>, type_list, AllOf...>>{}; - } - - /** - * @brief Adds an observing matcher to the collector. - * @tparam AnyOf Type of element for which changes should be detected. - * @return The updated collector. - */ - template - static constexpr auto update() noexcept { - return basic_collector, type_list<>, AnyOf>>{}; - } -}; - -/** - * @brief Collector. - * @copydetails basic_collector<> - * @tparam Reject Untracked types used to filter out entities. - * @tparam Require Untracked types required by the matcher. - * @tparam Rule Specific details of the current matcher. - * @tparam Other Other matchers. - */ -template -struct [[deprecated("use reactive mixin instead")]] basic_collector, type_list, Rule...>, Other...> { - /*! @brief Current matcher. */ - using current_type = matcher, type_list, Rule...>; - - /** - * @brief Adds a grouping matcher to the collector. - * @tparam AllOf Types of elements tracked by the matcher. - * @tparam NoneOf Types of elements used to filter out entities. - * @return The updated collector. - */ - template - static constexpr auto group(exclude_t = exclude_t{}) noexcept { - return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; - } - - /** - * @brief Adds an observing matcher to the collector. - * @tparam AnyOf Type of element for which changes should be detected. - * @return The updated collector. - */ - template - static constexpr auto update() noexcept { - return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; - } - - /** - * @brief Updates the filter of the last added matcher. - * @tparam AllOf Types of elements required by the matcher. - * @tparam NoneOf Types of elements used to filter out entities. - * @return The updated collector. - */ - template - static constexpr auto where(exclude_t = exclude_t{}) noexcept { - using extended_type = matcher, type_list, Rule...>; - return basic_collector{}; - } -}; - -/*! @brief Variable template used to ease the definition of collectors. */ -inline constexpr basic_collector<> collector{}; - -/** - * @brief Observer. - * - * An observer returns all the entities and only the entities that fit the - * requirements of at least one matcher. Moreover, it's guaranteed that the - * entity list is tightly packed in memory for fast iterations.
- * In general, observers don't stay true to the order of any set of elements. - * - * Observers work mainly with two types of matchers, provided through a - * collector: - * - * * Observing matcher: an observer will return at least all the living entities - * for which one or more of the given elements have been updated and not yet - * destroyed. - * * Grouping matcher: an observer will return at least all the living entities - * that would have entered the given group if it existed and that would have - * not yet left it. - * - * If an entity respects the requirements of multiple matchers, it will be - * returned once and only once by the observer in any case. - * - * Matchers support also filtering by means of a _where_ clause that accepts - * both a list of types and an exclusion list.
- * Whenever a matcher finds that an entity matches its requirements, the - * condition of the filter is verified before to register the entity itself. - * Moreover, a registered entity isn't returned by the observer if the condition - * set by the filter is broken in the meantime. - * - * @b Important - * - * Iterators aren't invalidated if: - * - * * New instances of the given elements are created and assigned to entities. - * * The entity currently pointed is modified (as an example, if one of the - * given elements is removed from the entity to which the iterator points). - * * The entity currently pointed is destroyed. - * - * In all the other cases, modifying the pools of the given elements in any way - * invalidates all the iterators. - * - * @warning - * Lifetime of an observer doesn't necessarily have to overcome that of the - * registry to which it is connected. However, the observer must be disconnected - * from the registry before being destroyed to avoid crashes due to dangling - * pointers. - * - * @tparam Registry Basic registry type. - * @tparam Allocator Type of allocator used to manage memory and elements. - */ -template -class basic_observer { - using mask_type = std::uint64_t; - using storage_type = basic_storage::template rebind_alloc>; - - template - static void discard_if(storage_type &storage, Registry &, const typename Registry::entity_type entt) { - if(storage.contains(entt) && !(storage.get(entt) &= (~(1 << Index)))) { - storage.erase(entt); - } - } - - template - struct matcher_handler; - - template - struct matcher_handler, type_list, AnyOf>> { - template - static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) { - if(parent.template all_of(entt) && !parent.template any_of(entt)) { - if(!storage.contains(entt)) { - storage.emplace(entt); - } - - storage.get(entt) |= (1 << Index); - } - } - - template - static void connect(storage_type &storage, Registry &parent) { - (parent.template on_destroy().template connect<&discard_if>(storage), ...); - (parent.template on_construct().template connect<&discard_if>(storage), ...); - parent.template on_update().template connect<&maybe_valid_if>(storage); - parent.template on_destroy().template connect<&discard_if>(storage); - } - - static void disconnect(storage_type &storage, Registry &parent) { - (parent.template on_destroy().disconnect(&storage), ...); - (parent.template on_construct().disconnect(&storage), ...); - parent.template on_update().disconnect(&storage); - parent.template on_destroy().disconnect(&storage); - } - }; - - template - struct matcher_handler, type_list, type_list, AllOf...>> { - template - static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) { - bool guard{}; - - if constexpr(sizeof...(Ignore) == 0) { - guard = parent.template all_of(entt) && !parent.template any_of(entt); - } else { - guard = parent.template all_of(entt) && ((std::is_same_v || !parent.template any_of(entt)) && ...) && !parent.template any_of(entt); - } - - if(guard) { - if(!storage.contains(entt)) { - storage.emplace(entt); - } - - storage.get(entt) |= (1 << Index); - } - } - - template - static void connect(storage_type &storage, Registry &parent) { - (parent.template on_destroy().template connect<&discard_if>(storage), ...); - (parent.template on_construct().template connect<&discard_if>(storage), ...); - (parent.template on_construct().template connect<&maybe_valid_if>(storage), ...); - (parent.template on_destroy().template connect<&maybe_valid_if>(storage), ...); - (parent.template on_destroy().template connect<&discard_if>(storage), ...); - (parent.template on_construct().template connect<&discard_if>(storage), ...); - } - - static void disconnect(storage_type &storage, Registry &parent) { - (parent.template on_destroy().disconnect(&storage), ...); - (parent.template on_construct().disconnect(&storage), ...); - (parent.template on_construct().disconnect(&storage), ...); - (parent.template on_destroy().disconnect(&storage), ...); - (parent.template on_destroy().disconnect(&storage), ...); - (parent.template on_construct().disconnect(&storage), ...); - } - }; - - template - static void disconnect(Registry &parent, storage_type &storage) { - (matcher_handler::disconnect(storage, parent), ...); - } - - template - static void connect(Registry &parent, storage_type &storage, std::index_sequence) { - static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); - (matcher_handler::template connect(storage, parent), ...); - } - -public: - /*! @brief Allocator type. */ - using allocator_type = Allocator; - /*! Basic registry type. */ - using registry_type = Registry; - /*! @brief Underlying entity identifier. */ - using entity_type = typename registry_type::entity_type; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Random access iterator type. */ - using iterator = typename registry_type::common_type::iterator; - - /*! @brief Default constructor. */ - basic_observer() - : basic_observer{allocator_type{}} {} - - /** - * @brief Constructs an empty storage with a given allocator. - * @param allocator The allocator to use. - */ - explicit basic_observer(const allocator_type &allocator) - : release{}, - parent{}, - storage{allocator} {} - - /*! @brief Default copy constructor, deleted on purpose. */ - basic_observer(const basic_observer &) = delete; - - /*! @brief Default move constructor, deleted on purpose. */ - basic_observer(basic_observer &&) = delete; - - /** - * @brief Creates an observer and connects it to a given registry. - * @tparam Matcher Types of matchers to use to initialize the observer. - * @param reg A valid reference to a registry. - * @param allocator The allocator to use. - */ - template - basic_observer(registry_type ®, basic_collector, const allocator_type &allocator = allocator_type{}) - : release{&basic_observer::disconnect}, - parent{®}, - storage{allocator} { - connect(reg, storage, std::index_sequence_for{}); - } - - /*! @brief Default destructor. */ - ~basic_observer() = default; - - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This observer. - */ - basic_observer &operator=(const basic_observer &) = delete; - - /** - * @brief Default move assignment operator, deleted on purpose. - * @return This observer. - */ - basic_observer &operator=(basic_observer &&) = delete; - - /** - * @brief Connects an observer to a given registry. - * @tparam Matcher Types of matchers to use to initialize the observer. - * @param reg A valid reference to a registry. - */ - template - void connect(registry_type ®, basic_collector) { - disconnect(); - storage.clear(); - - parent = ® - release = &basic_observer::disconnect; - connect(reg, storage, std::index_sequence_for{}); - } - - /*! @brief Disconnects an observer from the registry it keeps track of. */ - void disconnect() { - if(release) { - release(*parent, storage); - release = nullptr; - } - } - - /** - * @brief Returns the number of elements in an observer. - * @return Number of elements. - */ - [[nodiscard]] size_type size() const noexcept { - return storage.size(); - } - - /** - * @brief Checks whether an observer is empty. - * @return True if the observer is empty, false otherwise. - */ - [[nodiscard]] bool empty() const noexcept { - return storage.empty(); - } - - /** - * @brief Direct access to the list of entities of the observer. - * - * The returned pointer is such that range `[data(), data() + size())` is - * always a valid range, even if the container is empty. - * - * @note - * Entities are in the reverse order as returned by the `begin`/`end` - * iterators. - * - * @return A pointer to the array of entities. - */ - [[nodiscard]] const entity_type *data() const noexcept { - return storage.data(); - } - - /** - * @brief Returns an iterator to the first entity of the observer. - * - * If the observer is empty, the returned iterator will be equal to `end()`. - * - * @return An iterator to the first entity of the observer. - */ - [[nodiscard]] iterator begin() const noexcept { - return storage.storage_type::base_type::begin(); - } - - /** - * @brief Returns an iterator that is past the last entity of the observer. - * @return An iterator to the entity following the last entity of the - * observer. - */ - [[nodiscard]] iterator end() const noexcept { - return storage.storage_type::base_type::end(); - } - - /*! @brief Clears the underlying container. */ - void clear() noexcept { - storage.clear(); - } - - /** - * @brief Iterates entities and applies the given function object to them. - * - * The function object is invoked for each entity.
- * The signature of the function must be equivalent to the following form: - * - * @code{.cpp} - * void(const entity_type); - * @endcode - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - for(const auto entity: *this) { - func(entity); - } - } - - /** - * @brief Iterates entities and applies the given function object to them, - * then clears the observer. - * - * @sa each - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) { - std::as_const(*this).each(std::move(func)); - clear(); - } - -private: - void (*release)(registry_type &, storage_type &); - registry_type *parent; - storage_type storage; -}; - -} // namespace entt - -#endif diff --git a/src/entt/entt.hpp b/src/entt/entt.hpp index a00fed5e4..500dbc43c 100644 --- a/src/entt/entt.hpp +++ b/src/entt/entt.hpp @@ -28,7 +28,6 @@ #include "entity/handle.hpp" #include "entity/helper.hpp" #include "entity/mixin.hpp" -#include "entity/observer.hpp" #include "entity/organizer.hpp" #include "entity/ranges.hpp" #include "entity/registry.hpp" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c6ae30b40..3dbeb7b43 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -229,7 +229,6 @@ SETUP_BASIC_TEST(entity entt/entity/entity.cpp) SETUP_BASIC_TEST(group entt/entity/group.cpp) SETUP_BASIC_TEST(handle entt/entity/handle.cpp) SETUP_BASIC_TEST(helper entt/entity/helper.cpp) -SETUP_BASIC_TEST(observer entt/entity/observer.cpp) SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp) SETUP_BASIC_TEST(reactive_mixin entt/entity/reactive_mixin.cpp) SETUP_BASIC_TEST(registry entt/entity/registry.cpp) diff --git a/test/entt/entity/BUILD.bazel b/test/entt/entity/BUILD.bazel index 5963bf744..5b08e97a1 100644 --- a/test/entt/entity/BUILD.bazel +++ b/test/entt/entity/BUILD.bazel @@ -8,7 +8,6 @@ _TESTS = [ "group", "handle", "helper", - "observer", "organizer", "reactive_mixin", "registry", diff --git a/test/entt/entity/observer.cpp b/test/entt/entity/observer.cpp deleted file mode 100644 index e4e7ff16f..000000000 --- a/test/entt/entity/observer.cpp +++ /dev/null @@ -1,368 +0,0 @@ -#include -#include -#include -#include - -TEST(Observer, Functionalities) { - entt::registry registry; - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - entt::observer observer{registry, entt::collector.group()}; - - ASSERT_EQ(observer.size(), 0u); - ASSERT_TRUE(observer.empty()); - ASSERT_EQ(observer.begin(), observer.end()); - - const auto entity = registry.create(); - registry.emplace(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - ASSERT_NE(observer.begin(), observer.end()); - ASSERT_EQ(++observer.begin(), observer.end()); - ASSERT_EQ(*observer.begin(), entity); - - observer.clear(); - - ASSERT_EQ(observer.size(), 0u); - ASSERT_TRUE(observer.empty()); - - observer.disconnect(); - registry.erase(entity); - registry.emplace(entity); - - ASSERT_EQ(observer.size(), 0u); - ASSERT_TRUE(observer.empty()); -} - -TEST(Observer, AllOf) { - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - constexpr auto collector = entt::collector.group(entt::exclude).group(); - - entt::registry registry; - entt::observer observer{registry, collector}; - const auto entity = registry.create(); - - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - registry.emplace(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - - registry.emplace(entity); - - ASSERT_FALSE(observer.empty()); - - registry.erase(entity); - - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - registry.emplace(entity); - - ASSERT_FALSE(observer.empty()); - - registry.erase(entity); - - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - observer.clear(); - - ASSERT_TRUE(observer.empty()); - - observer.disconnect(); - registry.emplace_or_replace(entity); - registry.emplace_or_replace(entity); - registry.erase(entity); - - ASSERT_TRUE(observer.empty()); -} - -TEST(Observer, AllOfFiltered) { - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - constexpr auto collector = entt::collector.group().where(entt::exclude); - - entt::registry registry; - entt::observer observer{registry, collector}; - const auto entity = registry.create(); - - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - - ASSERT_EQ(observer.size(), 0u); - ASSERT_TRUE(observer.empty()); - - registry.erase(entity); - registry.emplace(entity); - registry.emplace(entity); - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.erase(entity); - registry.erase(entity); - registry.emplace(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.erase(entity); - - ASSERT_TRUE(observer.empty()); - - observer.disconnect(); - registry.erase(entity); - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); -} - -TEST(Observer, Observe) { - entt::registry registry; - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - entt::observer observer{registry, entt::collector.update().update()}; - const auto entity = registry.create(); - - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.emplace_or_replace(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - - observer.clear(); - registry.replace(entity); - - ASSERT_FALSE(observer.empty()); - - observer.clear(); - - ASSERT_TRUE(observer.empty()); - - observer.disconnect(); - registry.emplace_or_replace(entity); - registry.emplace_or_replace(entity); - - ASSERT_TRUE(observer.empty()); -} - -TEST(Observer, ObserveFiltered) { - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - constexpr auto collector = entt::collector.update().where(entt::exclude); - - entt::registry registry; - entt::observer observer{registry, collector}; - const auto entity = registry.create(); - - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - registry.replace(entity); - - ASSERT_EQ(observer.size(), 0u); - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - registry.emplace(entity); - registry.replace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.erase(entity); - registry.replace(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.erase(entity); - - ASSERT_TRUE(observer.empty()); - - observer.disconnect(); - registry.replace(entity); - - ASSERT_TRUE(observer.empty()); -} - -TEST(Observer, AllOfObserve) { - entt::registry registry; - entt::observer observer{}; - const auto entity = registry.create(); - - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - observer.connect(registry, entt::collector.group().update()); - - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - registry.emplace(entity); - registry.replace(entity); - registry.erase(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - - registry.erase(entity); - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.replace(entity); - observer.clear(); - - ASSERT_TRUE(observer.empty()); - - observer.disconnect(); - registry.emplace_or_replace(entity); - registry.emplace_or_replace(entity); - - ASSERT_TRUE(observer.empty()); -} - -TEST(Observer, CrossRulesCornerCase) { - entt::registry registry; - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - entt::observer observer{registry, entt::collector.group().group()}; - const auto entity = registry.create(); - - registry.emplace(entity); - observer.clear(); - - ASSERT_TRUE(observer.empty()); - - registry.emplace(entity); - registry.erase(entity); - - ASSERT_FALSE(observer.empty()); -} - -TEST(Observer, Each) { - entt::registry registry; - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - entt::observer observer{registry, entt::collector.group()}; - const auto entity = registry.create(); - registry.emplace(entity); - - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(observer.size(), 1u); - - std::as_const(observer).each([entity](const auto entt) { - ASSERT_EQ(entity, entt); - }); - - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(observer.size(), 1u); - - observer.each([entity](const auto entt) { - ASSERT_EQ(entity, entt); - }); - - ASSERT_TRUE(observer.empty()); - ASSERT_EQ(observer.size(), 0u); -} - -TEST(Observer, MultipleFilters) { - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - constexpr auto collector = entt::collector.update().where().update().where(); - - entt::registry registry; - entt::observer observer{registry, collector}; - const auto entity = registry.create(); - - ASSERT_TRUE(observer.empty()); - - registry.emplace_or_replace(entity); - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.emplace_or_replace(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - - observer.clear(); - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.emplace_or_replace(entity); - registry.emplace(entity); - - ASSERT_TRUE(observer.empty()); - - registry.emplace_or_replace(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - - registry.erase(entity); - - ASSERT_TRUE(observer.empty()); - - registry.emplace_or_replace(entity); - - ASSERT_EQ(observer.size(), 1u); - ASSERT_FALSE(observer.empty()); - ASSERT_EQ(*observer.data(), entity); - - observer.clear(); - observer.disconnect(); - - registry.emplace_or_replace(entity); - - ASSERT_TRUE(observer.empty()); -} - -TEST(Observer, GroupCornerCase) { - // NOLINTBEGIN(readability-static-accessed-through-instance) - constexpr auto add_collector = entt::collector.group(entt::exclude); - constexpr auto remove_collector = entt::collector.group(); - // NOLINTEND(readability-static-accessed-through-instance) - - entt::registry registry; - entt::observer add_observer{registry, add_collector}; - entt::observer remove_observer{registry, remove_collector}; - - const auto entity = registry.create(); - registry.emplace(entity); - - ASSERT_FALSE(add_observer.empty()); - ASSERT_TRUE(remove_observer.empty()); - - add_observer.clear(); - registry.emplace(entity); - - ASSERT_TRUE(add_observer.empty()); - ASSERT_FALSE(remove_observer.empty()); - - remove_observer.clear(); - registry.erase(entity); - - ASSERT_FALSE(add_observer.empty()); - ASSERT_TRUE(remove_observer.empty()); -}