Parcourir la source

observer: so long, and thanks for all the fish

Michele Caini il y a 1 an
Parent
commit
15b9871205

+ 0 - 1
CMakeLists.txt

@@ -145,7 +145,6 @@ if(ENTT_INCLUDE_HEADERS)
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/mixin.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
-            $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/ranges.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/registry.hpp>

+ 0 - 1
entt.imp

@@ -20,7 +20,6 @@
   { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
   { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
   { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/mixin.hpp>", "public" ] },
-  { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/observer.hpp>", "public" ] },
   { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
   { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/ranges.hpp>", "public" ] },
   { "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },

+ 0 - 6
src/entt/entity/fwd.hpp

@@ -47,9 +47,6 @@ class basic_runtime_view;
 template<typename, typename, typename>
 class basic_group;
 
-template<typename, typename = std::allocator<void>>
-class basic_observer;
-
 template<typename>
 class basic_organizer;
 
@@ -92,9 +89,6 @@ using reactive_mixin = basic_reactive_mixin<Type, basic_registry<typename Type::
 /*! @brief Alias declaration for the most common use case. */
 using registry = basic_registry<>;
 
-/*! @brief Alias declaration for the most common use case. */
-using observer = basic_observer<registry>;
-
 /*! @brief Alias declaration for the most common use case. */
 using organizer = basic_organizer<registry>;
 

+ 0 - 438
src/entt/entity/observer.hpp

@@ -1,438 +0,0 @@
-#ifndef ENTT_ENTITY_OBSERVER_HPP
-#define ENTT_ENTITY_OBSERVER_HPP
-
-#include <cstddef>
-#include <cstdint>
-#include <limits>
-#include <type_traits>
-#include <utility>
-#include "../core/type_traits.hpp"
-#include "fwd.hpp"
-#include "storage.hpp"
-
-namespace entt {
-
-/*! @brief Grouping matcher. */
-template<typename...>
-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<typename...>
-struct basic_collector;
-
-/**
- * @brief Collector.
- *
- * A collector contains a set of rules (literally, matchers) to use to track
- * entities.<br/>
- * 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<typename... AllOf, typename... NoneOf>
-    static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
-        return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, 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<typename AnyOf>
-    static constexpr auto update() noexcept {
-        return basic_collector<matcher<type_list<>, 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<typename... Reject, typename... Require, typename... Rule, typename... Other>
-struct [[deprecated("use reactive mixin instead")]] basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
-    /*! @brief Current matcher. */
-    using current_type = matcher<type_list<Reject...>, type_list<Require...>, 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<typename... AllOf, typename... NoneOf>
-    static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
-        return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, 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<typename AnyOf>
-    static constexpr auto update() noexcept {
-        return basic_collector<matcher<type_list<>, 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<typename... AllOf, typename... NoneOf>
-    static constexpr auto where(exclude_t<NoneOf...> = exclude_t{}) noexcept {
-        using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
-        return basic_collector<extended_type, Other...>{};
-    }
-};
-
-/*! @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.<br/>
- * 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.<br/>
- * 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<typename Registry, typename Allocator>
-class basic_observer {
-    using mask_type = std::uint64_t;
-    using storage_type = basic_storage<mask_type, typename Registry::entity_type, typename std::allocator_traits<Allocator>::template rebind_alloc<mask_type>>;
-
-    template<std::size_t Index>
-    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<typename>
-    struct matcher_handler;
-
-    template<typename... Reject, typename... Require, typename AnyOf>
-    struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
-        template<std::size_t Index>
-        static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) {
-            if(parent.template all_of<Require...>(entt) && !parent.template any_of<Reject...>(entt)) {
-                if(!storage.contains(entt)) {
-                    storage.emplace(entt);
-                }
-
-                storage.get(entt) |= (1 << Index);
-            }
-        }
-
-        template<std::size_t Index>
-        static void connect(storage_type &storage, Registry &parent) {
-            (parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...);
-            (parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...);
-            parent.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(storage);
-            parent.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(storage);
-        }
-
-        static void disconnect(storage_type &storage, Registry &parent) {
-            (parent.template on_destroy<Require>().disconnect(&storage), ...);
-            (parent.template on_construct<Reject>().disconnect(&storage), ...);
-            parent.template on_update<AnyOf>().disconnect(&storage);
-            parent.template on_destroy<AnyOf>().disconnect(&storage);
-        }
-    };
-
-    template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
-    struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
-        template<std::size_t Index, typename... Ignore>
-        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<AllOf..., Require...>(entt) && !parent.template any_of<NoneOf..., Reject...>(entt);
-            } else {
-                guard = parent.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !parent.template any_of<NoneOf>(entt)) && ...) && !parent.template any_of<Reject...>(entt);
-            }
-
-            if(guard) {
-                if(!storage.contains(entt)) {
-                    storage.emplace(entt);
-                }
-
-                storage.get(entt) |= (1 << Index);
-            }
-        }
-
-        template<std::size_t Index>
-        static void connect(storage_type &storage, Registry &parent) {
-            (parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...);
-            (parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...);
-            (parent.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(storage), ...);
-            (parent.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(storage), ...);
-            (parent.template on_destroy<AllOf>().template connect<&discard_if<Index>>(storage), ...);
-            (parent.template on_construct<NoneOf>().template connect<&discard_if<Index>>(storage), ...);
-        }
-
-        static void disconnect(storage_type &storage, Registry &parent) {
-            (parent.template on_destroy<Require>().disconnect(&storage), ...);
-            (parent.template on_construct<Reject>().disconnect(&storage), ...);
-            (parent.template on_construct<AllOf>().disconnect(&storage), ...);
-            (parent.template on_destroy<NoneOf>().disconnect(&storage), ...);
-            (parent.template on_destroy<AllOf>().disconnect(&storage), ...);
-            (parent.template on_construct<NoneOf>().disconnect(&storage), ...);
-        }
-    };
-
-    template<typename... Matcher>
-    static void disconnect(Registry &parent, storage_type &storage) {
-        (matcher_handler<Matcher>::disconnect(storage, parent), ...);
-    }
-
-    template<typename... Matcher, std::size_t... Index>
-    static void connect(Registry &parent, storage_type &storage, std::index_sequence<Index...>) {
-        static_assert(sizeof...(Matcher) < std::numeric_limits<mask_type>::digits, "Too many matchers");
-        (matcher_handler<Matcher>::template connect<Index>(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<typename... Matcher>
-    basic_observer(registry_type &reg, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
-        : release{&basic_observer::disconnect<Matcher...>},
-          parent{&reg},
-          storage{allocator} {
-        connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{});
-    }
-
-    /*! @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<typename... Matcher>
-    void connect(registry_type &reg, basic_collector<Matcher...>) {
-        disconnect();
-        storage.clear();
-
-        parent = &reg;
-        release = &basic_observer::disconnect<Matcher...>;
-        connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{});
-    }
-
-    /*! @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.<br/>
-     * 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<typename Func>
-    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<typename Func>
-    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

+ 0 - 1
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"

+ 0 - 1
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)

+ 0 - 1
test/entt/entity/BUILD.bazel

@@ -8,7 +8,6 @@ _TESTS = [
     "group",
     "handle",
     "helper",
-    "observer",
     "organizer",
     "reactive_mixin",
     "registry",

+ 0 - 368
test/entt/entity/observer.cpp

@@ -1,368 +0,0 @@
-#include <utility>
-#include <gtest/gtest.h>
-#include <entt/entity/observer.hpp>
-#include <entt/entity/registry.hpp>
-
-TEST(Observer, Functionalities) {
-    entt::registry registry;
-    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
-    entt::observer observer{registry, entt::collector.group<int>()};
-
-    ASSERT_EQ(observer.size(), 0u);
-    ASSERT_TRUE(observer.empty());
-    ASSERT_EQ(observer.begin(), observer.end());
-
-    const auto entity = registry.create();
-    registry.emplace<int>(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<int>(entity);
-    registry.emplace<int>(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<int, char>(entt::exclude<float>).group<int, double>();
-
-    entt::registry registry;
-    entt::observer observer{registry, collector};
-    const auto entity = registry.create();
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<int>(entity);
-    registry.emplace<char>(entity);
-
-    ASSERT_EQ(observer.size(), 1u);
-    ASSERT_FALSE(observer.empty());
-    ASSERT_EQ(*observer.data(), entity);
-
-    registry.emplace<double>(entity);
-
-    ASSERT_FALSE(observer.empty());
-
-    registry.erase<int>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<float>(entity);
-    registry.emplace<int>(entity);
-
-    ASSERT_FALSE(observer.empty());
-
-    registry.erase<double>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<double>(entity);
-    observer.clear();
-
-    ASSERT_TRUE(observer.empty());
-
-    observer.disconnect();
-    registry.emplace_or_replace<int>(entity);
-    registry.emplace_or_replace<char>(entity);
-    registry.erase<float>(entity);
-
-    ASSERT_TRUE(observer.empty());
-}
-
-TEST(Observer, AllOfFiltered) {
-    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
-    constexpr auto collector = entt::collector.group<int>().where<char>(entt::exclude<double>);
-
-    entt::registry registry;
-    entt::observer observer{registry, collector};
-    const auto entity = registry.create();
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<int>(entity);
-
-    ASSERT_EQ(observer.size(), 0u);
-    ASSERT_TRUE(observer.empty());
-
-    registry.erase<int>(entity);
-    registry.emplace<char>(entity);
-    registry.emplace<double>(entity);
-    registry.emplace<int>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.erase<int>(entity);
-    registry.erase<double>(entity);
-    registry.emplace<int>(entity);
-
-    ASSERT_EQ(observer.size(), 1u);
-    ASSERT_FALSE(observer.empty());
-    ASSERT_EQ(*observer.data(), entity);
-
-    registry.emplace<double>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.erase<double>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    observer.disconnect();
-    registry.erase<int>(entity);
-    registry.emplace<int>(entity);
-
-    ASSERT_TRUE(observer.empty());
-}
-
-TEST(Observer, Observe) {
-    entt::registry registry;
-    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
-    entt::observer observer{registry, entt::collector.update<int>().update<char>()};
-    const auto entity = registry.create();
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<int>(entity);
-    registry.emplace<char>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace_or_replace<int>(entity);
-
-    ASSERT_EQ(observer.size(), 1u);
-    ASSERT_FALSE(observer.empty());
-    ASSERT_EQ(*observer.data(), entity);
-
-    observer.clear();
-    registry.replace<char>(entity);
-
-    ASSERT_FALSE(observer.empty());
-
-    observer.clear();
-
-    ASSERT_TRUE(observer.empty());
-
-    observer.disconnect();
-    registry.emplace_or_replace<int>(entity);
-    registry.emplace_or_replace<char>(entity);
-
-    ASSERT_TRUE(observer.empty());
-}
-
-TEST(Observer, ObserveFiltered) {
-    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
-    constexpr auto collector = entt::collector.update<int>().where<char>(entt::exclude<double>);
-
-    entt::registry registry;
-    entt::observer observer{registry, collector};
-    const auto entity = registry.create();
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<int>(entity);
-    registry.replace<int>(entity);
-
-    ASSERT_EQ(observer.size(), 0u);
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<char>(entity);
-    registry.emplace<double>(entity);
-    registry.replace<int>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.erase<double>(entity);
-    registry.replace<int>(entity);
-
-    ASSERT_EQ(observer.size(), 1u);
-    ASSERT_FALSE(observer.empty());
-    ASSERT_EQ(*observer.data(), entity);
-
-    registry.emplace<double>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.erase<double>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    observer.disconnect();
-    registry.replace<int>(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<int>().update<char>());
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<int>(entity);
-    registry.emplace<char>(entity);
-    registry.replace<char>(entity);
-    registry.erase<int>(entity);
-
-    ASSERT_EQ(observer.size(), 1u);
-    ASSERT_FALSE(observer.empty());
-    ASSERT_EQ(*observer.data(), entity);
-
-    registry.erase<char>(entity);
-    registry.emplace<char>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.replace<char>(entity);
-    observer.clear();
-
-    ASSERT_TRUE(observer.empty());
-
-    observer.disconnect();
-    registry.emplace_or_replace<int>(entity);
-    registry.emplace_or_replace<char>(entity);
-
-    ASSERT_TRUE(observer.empty());
-}
-
-TEST(Observer, CrossRulesCornerCase) {
-    entt::registry registry;
-    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
-    entt::observer observer{registry, entt::collector.group<int>().group<char>()};
-    const auto entity = registry.create();
-
-    registry.emplace<int>(entity);
-    observer.clear();
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace<char>(entity);
-    registry.erase<int>(entity);
-
-    ASSERT_FALSE(observer.empty());
-}
-
-TEST(Observer, Each) {
-    entt::registry registry;
-    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
-    entt::observer observer{registry, entt::collector.group<int>()};
-    const auto entity = registry.create();
-    registry.emplace<int>(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<int>().where<char>().update<double>().where<float>();
-
-    entt::registry registry;
-    entt::observer observer{registry, collector};
-    const auto entity = registry.create();
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace_or_replace<int>(entity);
-    registry.emplace<char>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace_or_replace<int>(entity);
-
-    ASSERT_EQ(observer.size(), 1u);
-    ASSERT_FALSE(observer.empty());
-    ASSERT_EQ(*observer.data(), entity);
-
-    observer.clear();
-    registry.emplace<double>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace_or_replace<double>(entity);
-    registry.emplace<float>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace_or_replace<double>(entity);
-
-    ASSERT_EQ(observer.size(), 1u);
-    ASSERT_FALSE(observer.empty());
-    ASSERT_EQ(*observer.data(), entity);
-
-    registry.erase<float>(entity);
-
-    ASSERT_TRUE(observer.empty());
-
-    registry.emplace_or_replace<int>(entity);
-
-    ASSERT_EQ(observer.size(), 1u);
-    ASSERT_FALSE(observer.empty());
-    ASSERT_EQ(*observer.data(), entity);
-
-    observer.clear();
-    observer.disconnect();
-
-    registry.emplace_or_replace<int>(entity);
-
-    ASSERT_TRUE(observer.empty());
-}
-
-TEST(Observer, GroupCornerCase) {
-    // NOLINTBEGIN(readability-static-accessed-through-instance)
-    constexpr auto add_collector = entt::collector.group<int>(entt::exclude<char>);
-    constexpr auto remove_collector = entt::collector.group<int, char>();
-    // 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<int>(entity);
-
-    ASSERT_FALSE(add_observer.empty());
-    ASSERT_TRUE(remove_observer.empty());
-
-    add_observer.clear();
-    registry.emplace<char>(entity);
-
-    ASSERT_TRUE(add_observer.empty());
-    ASSERT_FALSE(remove_observer.empty());
-
-    remove_observer.clear();
-    registry.erase<char>(entity);
-
-    ASSERT_FALSE(add_observer.empty());
-    ASSERT_TRUE(remove_observer.empty());
-}