Explorar el Código

Updated EnTT dependency to v3.12.2
Changed the entity registry view typedefs for updated EnTT in EntityViewDefinitions, RendererScene
Added a way to toggle rendering for individual meshes, in GraphicsDataSets, ModelComponent, SceneLoader
Added a way to change the prefab in MetadataComponent
Added a way to change the light type in LightComponent
Added checking for Z buffer near and far value changes for projection matrix recalculation in RendererFrontend
Added shutdown methods for every scene that get called during system destruction
Added component removal pipeline that performs explicit memory release in every System Scene, System
Added a way to adjust luminance multiplier in ShaderUniforms, ShaderUniformUpdater, Tonemapping shader
Added shortcut keybinds for "File" operations in EditorWindow
Added next-available entity ID and entity name generation during new entity creation in EditorWindow
Added prefab selection during new entity creation in EditorWindow
Added more scene and graphics settings in EditorWindow
Added more Config variables
Added more change types
Fixed a bug of parallax mapping LOD calculation being wrong in Geometry Pass shader
Fixed a bug of changes being sent to objects that have been removed during the same frame, by distributing data-changed after regular changes in ChangeController
Fixed a bug of scene being loaded with an old filename during scene reload in Engine
Fixed a bug of components calling deleted scene during component destruction, by deleting the entity registry before deleting the scenes in EngineState
Fixed a bug of entity ID not being set for LuaComponent during construction, in LuaComponent
Fixed a bug of default model being deleted because its reference counter reached 0 after all models are loaded, by storing a model handle for the default model, in ModelLoader
Fixed a bug of map file not being replaced during scene saving, by setting the stream output mode to truncate, in PropertyLoader

Paul A hace 2 años
padre
commit
ffc81b87a6
Se han modificado 100 ficheros con 11102 adiciones y 7516 borrados
  1. 1 0
      .gitignore
  2. 33 17
      Dependencies/include/entt/config/config.h
  3. 7 0
      Dependencies/include/entt/config/macro.h
  4. 7 1
      Dependencies/include/entt/config/version.h
  5. 312 237
      Dependencies/include/entt/container/dense_map.hpp
  6. 321 237
      Dependencies/include/entt/container/dense_set.hpp
  7. 5 3
      Dependencies/include/entt/container/fwd.hpp
  8. 4 3
      Dependencies/include/entt/core/algorithm.hpp
  9. 138 111
      Dependencies/include/entt/core/any.hpp
  10. 42 32
      Dependencies/include/entt/core/compressed_pair.hpp
  11. 23 38
      Dependencies/include/entt/core/enum.hpp
  12. 2 2
      Dependencies/include/entt/core/family.hpp
  13. 2 2
      Dependencies/include/entt/core/fwd.hpp
  14. 100 80
      Dependencies/include/entt/core/hashed_string.hpp
  15. 11 35
      Dependencies/include/entt/core/ident.hpp
  16. 115 34
      Dependencies/include/entt/core/iterator.hpp
  17. 211 28
      Dependencies/include/entt/core/memory.hpp
  18. 2 2
      Dependencies/include/entt/core/monostate.hpp
  19. 76 2
      Dependencies/include/entt/core/tuple.hpp
  20. 41 34
      Dependencies/include/entt/core/type_info.hpp
  21. 322 66
      Dependencies/include/entt/core/type_traits.hpp
  22. 13 13
      Dependencies/include/entt/core/utility.hpp
  23. 45 17
      Dependencies/include/entt/entity/component.hpp
  24. 101 56
      Dependencies/include/entt/entity/entity.hpp
  25. 184 35
      Dependencies/include/entt/entity/fwd.hpp
  26. 486 330
      Dependencies/include/entt/entity/group.hpp
  27. 128 83
      Dependencies/include/entt/entity/handle.hpp
  28. 190 73
      Dependencies/include/entt/entity/helper.hpp
  29. 302 0
      Dependencies/include/entt/entity/mixin.hpp
  30. 77 77
      Dependencies/include/entt/entity/observer.hpp
  31. 74 149
      Dependencies/include/entt/entity/organizer.hpp
  32. 312 421
      Dependencies/include/entt/entity/registry.hpp
  33. 116 72
      Dependencies/include/entt/entity/runtime_view.hpp
  34. 210 311
      Dependencies/include/entt/entity/snapshot.hpp
  35. 385 215
      Dependencies/include/entt/entity/sparse_set.hpp
  36. 287 260
      Dependencies/include/entt/entity/storage.hpp
  37. 0 52
      Dependencies/include/entt/entity/utility.hpp
  38. 346 294
      Dependencies/include/entt/entity/view.hpp
  39. 12 5
      Dependencies/include/entt/entt.hpp
  40. 4 0
      Dependencies/include/entt/fwd.hpp
  41. 349 0
      Dependencies/include/entt/graph/adjacency_matrix.hpp
  42. 58 0
      Dependencies/include/entt/graph/dot.hpp
  43. 341 0
      Dependencies/include/entt/graph/flow.hpp
  44. 27 0
      Dependencies/include/entt/graph/fwd.hpp
  45. 90 49
      Dependencies/include/entt/locator/locator.hpp
  46. 281 161
      Dependencies/include/entt/meta/container.hpp
  47. 67 0
      Dependencies/include/entt/meta/context.hpp
  48. 0 57
      Dependencies/include/entt/meta/ctx.hpp
  49. 306 376
      Dependencies/include/entt/meta/factory.hpp
  50. 400 303
      Dependencies/include/entt/meta/meta.hpp
  51. 181 138
      Dependencies/include/entt/meta/node.hpp
  52. 20 4
      Dependencies/include/entt/meta/policy.hpp
  53. 96 69
      Dependencies/include/entt/meta/range.hpp
  54. 57 18
      Dependencies/include/entt/meta/resolve.hpp
  55. 2 2
      Dependencies/include/entt/meta/template.hpp
  56. 0 1
      Dependencies/include/entt/meta/type_traits.hpp
  57. 243 97
      Dependencies/include/entt/meta/utility.hpp
  58. 3 3
      Dependencies/include/entt/poly/fwd.hpp
  59. 24 25
      Dependencies/include/entt/poly/poly.hpp
  60. 20 0
      Dependencies/include/entt/process/fwd.hpp
  61. 14 14
      Dependencies/include/entt/process/process.hpp
  62. 179 109
      Dependencies/include/entt/process/scheduler.hpp
  63. 319 181
      Dependencies/include/entt/resource/cache.hpp
  64. 6 4
      Dependencies/include/entt/resource/fwd.hpp
  65. 0 183
      Dependencies/include/entt/resource/handle.hpp
  66. 14 42
      Dependencies/include/entt/resource/loader.hpp
  67. 245 0
      Dependencies/include/entt/resource/resource.hpp
  68. 69 60
      Dependencies/include/entt/signal/delegate.hpp
  69. 270 141
      Dependencies/include/entt/signal/dispatcher.hpp
  70. 95 237
      Dependencies/include/entt/signal/emitter.hpp
  71. 21 3
      Dependencies/include/entt/signal/fwd.hpp
  72. 80 222
      Dependencies/include/entt/signal/sigh.hpp
  73. 617 324
      Praxis3D/Data/Maps/componentTest.pmap
  74. 9 7
      Praxis3D/Data/Prefabs/cube1.prefab
  75. 6 23
      Praxis3D/Data/Shaders/geometryPass.frag
  76. 2 1
      Praxis3D/Data/Shaders/tonemapping.frag
  77. 1 1
      Praxis3D/Data/config.ini
  78. 69 25
      Praxis3D/Source/AudioScene.cpp
  79. 2 0
      Praxis3D/Source/AudioScene.h
  80. 3 0
      Praxis3D/Source/AudioSystem.h
  81. 71 79
      Praxis3D/Source/ChangeController.cpp
  82. 2 2
      Praxis3D/Source/ChangeController.h
  83. 2 1
      Praxis3D/Source/CommonDefinitions.h
  84. 4 1
      Praxis3D/Source/Config.cpp
  85. 12 5
      Praxis3D/Source/Config.h
  86. 1165 1076
      Praxis3D/Source/EditorWindow.cpp
  87. 67 1
      Praxis3D/Source/EditorWindow.h
  88. 4 1
      Praxis3D/Source/Engine.cpp
  89. 17 9
      Praxis3D/Source/EngineState.cpp
  90. 2 0
      Praxis3D/Source/EngineState.h
  91. 2 1
      Praxis3D/Source/EntityViewDefinitions.h
  92. 31 11
      Praxis3D/Source/GUIScene.cpp
  93. 2 0
      Praxis3D/Source/GUIScene.h
  94. 3 0
      Praxis3D/Source/GUISystem.h
  95. 5 2
      Praxis3D/Source/GraphicsDataSets.h
  96. 59 17
      Praxis3D/Source/LightComponent.h
  97. 1 0
      Praxis3D/Source/LoaderBase.h
  98. 1 1
      Praxis3D/Source/LuaComponent.h
  99. 5 0
      Praxis3D/Source/MetadataComponent.h
  100. 14 2
      Praxis3D/Source/ModelComponent.h

+ 1 - 0
.gitignore

@@ -41,3 +41,4 @@ compileData.scor
 compileData.scor.gbl
 compileData.scor.incl
 compileData.scor.t0000
+*.glb

+ 33 - 17
Dependencies/include/entt/config/config.h

@@ -1,35 +1,32 @@
 #ifndef ENTT_CONFIG_CONFIG_H
 #define ENTT_CONFIG_CONFIG_H
 
+#include "version.h"
+
 #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
-#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_CONSTEXPR
 #    define ENTT_THROW throw
 #    define ENTT_TRY try
 #    define ENTT_CATCH catch(...)
 #else
-#    define ENTT_NOEXCEPT
+#    define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20)
 #    define ENTT_THROW
 #    define ENTT_TRY if(true)
 #    define ENTT_CATCH if(false)
 #endif
 
-#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
-#    include <new>
-#    define ENTT_LAUNDER(expr) std::launder(expr)
-#else
-#    define ENTT_LAUNDER(expr) expr
-#endif
-
-#ifndef ENTT_USE_ATOMIC
-#    define ENTT_MAYBE_ATOMIC(Type) Type
-#else
+#ifdef ENTT_USE_ATOMIC
 #    include <atomic>
 #    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
 #endif
 
 #ifndef ENTT_ID_TYPE
 #    include <cstdint>
 #    define ENTT_ID_TYPE std::uint32_t
+#else
+#    include <cstdint> // provides coverage for types in the std namespace
 #endif
 
 #ifndef ENTT_SPARSE_PAGE
@@ -42,19 +39,31 @@
 
 #ifdef ENTT_DISABLE_ASSERT
 #    undef ENTT_ASSERT
-#    define ENTT_ASSERT(...) (void(0))
+#    define ENTT_ASSERT(condition, msg) (void(0))
 #elif !defined ENTT_ASSERT
 #    include <cassert>
-#    define ENTT_ASSERT(condition, ...) assert(condition)
+#    define ENTT_ASSERT(condition, msg) assert(condition)
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT_CONSTEXPR
+#    define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0))
+#elif !defined ENTT_ASSERT_CONSTEXPR
+#    define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg)
 #endif
 
+#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg);
+
 #ifdef ENTT_NO_ETO
-#    define ENTT_IGNORE_IF_EMPTY false
+#    define ENTT_ETO_TYPE(Type) void
 #else
-#    define ENTT_IGNORE_IF_EMPTY true
+#    define ENTT_ETO_TYPE(Type) Type
 #endif
 
-#ifndef ENTT_STANDARD_CPP
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
 #    if defined __clang__ || defined __GNUC__
 #        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
 #        define ENTT_PRETTY_FUNCTION_PREFIX '='
@@ -66,4 +75,11 @@
 #    endif
 #endif
 
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
 #endif

+ 7 - 0
Dependencies/include/entt/config/macro.h

@@ -0,0 +1,7 @@
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif

+ 7 - 1
Dependencies/include/entt/config/version.h

@@ -1,8 +1,14 @@
 #ifndef ENTT_CONFIG_VERSION_H
 #define ENTT_CONFIG_VERSION_H
 
+#include "macro.h"
+
 #define ENTT_VERSION_MAJOR 3
-#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_MINOR 13
 #define ENTT_VERSION_PATCH 0
 
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
 #endif

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 312 - 237
Dependencies/include/entt/container/dense_map.hpp


+ 321 - 237
Dependencies/include/entt/container/dense_hash_set.hpp → Dependencies/include/entt/container/dense_set.hpp

@@ -1,7 +1,6 @@
-#ifndef ENTT_CONTAINER_DENSE_HASH_SET_HPP
-#define ENTT_CONTAINER_DENSE_HASH_SET_HPP
+#ifndef ENTT_CONTAINER_DENSE_SET_HPP
+#define ENTT_CONTAINER_DENSE_SET_HPP
 
-#include <algorithm>
 #include <cmath>
 #include <cstddef>
 #include <functional>
@@ -27,177 +26,167 @@ namespace entt {
 
 namespace internal {
 
-template<typename Type>
-struct dense_hash_set_node final {
-    template<typename... Args>
-    dense_hash_set_node(const std::size_t pos, Args &&...args)
-        : next{pos},
-          element{std::forward<Args>(args)...} {}
-
-    std::size_t next;
-    Type element;
-};
-
 template<typename It>
-class dense_hash_set_iterator {
-    friend dense_hash_set_iterator<const std::remove_pointer_t<It> *>;
-
-    using iterator_traits = std::iterator_traits<decltype(std::addressof(std::as_const(std::declval<It>()->element)))>;
+class dense_set_iterator final {
+    template<typename>
+    friend class dense_set_iterator;
 
 public:
-    using value_type = typename iterator_traits::value_type;
-    using pointer = typename iterator_traits::pointer;
-    using reference = typename iterator_traits::reference;
-    using difference_type = typename iterator_traits::difference_type;
+    using value_type = typename It::value_type::second_type;
+    using pointer = const value_type *;
+    using reference = const value_type &;
+    using difference_type = std::ptrdiff_t;
     using iterator_category = std::random_access_iterator_tag;
 
-    dense_hash_set_iterator() ENTT_NOEXCEPT = default;
+    constexpr dense_set_iterator() noexcept
+        : it{} {}
 
-    dense_hash_set_iterator(const It iter) ENTT_NOEXCEPT
+    constexpr dense_set_iterator(const It iter) noexcept
         : it{iter} {}
 
-    template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
-    dense_hash_set_iterator(const dense_hash_set_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
+    template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+    constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept
         : it{other.it} {}
 
-    dense_hash_set_iterator &operator++() ENTT_NOEXCEPT {
+    constexpr dense_set_iterator &operator++() noexcept {
         return ++it, *this;
     }
 
-    dense_hash_set_iterator operator++(int) ENTT_NOEXCEPT {
-        dense_hash_set_iterator orig = *this;
+    constexpr dense_set_iterator operator++(int) noexcept {
+        dense_set_iterator orig = *this;
         return ++(*this), orig;
     }
 
-    dense_hash_set_iterator &operator--() ENTT_NOEXCEPT {
+    constexpr dense_set_iterator &operator--() noexcept {
         return --it, *this;
     }
 
-    dense_hash_set_iterator operator--(int) ENTT_NOEXCEPT {
-        dense_hash_set_iterator orig = *this;
+    constexpr dense_set_iterator operator--(int) noexcept {
+        dense_set_iterator orig = *this;
         return operator--(), orig;
     }
 
-    dense_hash_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+    constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
         it += value;
         return *this;
     }
 
-    dense_hash_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
-        dense_hash_set_iterator copy = *this;
+    constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
+        dense_set_iterator copy = *this;
         return (copy += value);
     }
 
-    dense_hash_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+    constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
         return (*this += -value);
     }
 
-    dense_hash_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+    constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
         return (*this + -value);
     }
 
-    [[nodiscard]] reference operator[](const difference_type value) const {
-        return it[value].element;
+    [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
+        return it[value].second;
     }
 
-    [[nodiscard]] pointer operator->() const {
-        return std::addressof(it->element);
+    [[nodiscard]] constexpr pointer operator->() const noexcept {
+        return std::addressof(it->second);
     }
 
-    [[nodiscard]] reference operator*() const {
+    [[nodiscard]] constexpr reference operator*() const noexcept {
         return *operator->();
     }
 
-    template<typename ILhs, typename IRhs>
-    friend auto operator-(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+    template<typename Lhs, typename Rhs>
+    friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
 
-    template<typename ILhs, typename IRhs>
-    friend bool operator==(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+    template<typename Lhs, typename Rhs>
+    friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
 
-    template<typename ILhs, typename IRhs>
-    friend bool operator<(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+    template<typename Lhs, typename Rhs>
+    friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
 
 private:
     It it;
 };
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] auto operator-(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
     return lhs.it - rhs.it;
 }
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] bool operator==(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
     return lhs.it == rhs.it;
 }
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] bool operator!=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
     return !(lhs == rhs);
 }
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] bool operator<(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
     return lhs.it < rhs.it;
 }
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] bool operator>(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
     return rhs < lhs;
 }
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] bool operator<=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
     return !(lhs > rhs);
 }
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] bool operator>=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
     return !(lhs < rhs);
 }
 
 template<typename It>
-class dense_hash_set_local_iterator {
-    friend dense_hash_set_local_iterator<const std::remove_pointer_t<It> *>;
-
-    using iterator_traits = std::iterator_traits<decltype(std::addressof(std::as_const(std::declval<It>()->element)))>;
+class dense_set_local_iterator final {
+    template<typename>
+    friend class dense_set_local_iterator;
 
 public:
-    using value_type = typename iterator_traits::value_type;
-    using pointer = typename iterator_traits::pointer;
-    using reference = typename iterator_traits::reference;
-    using difference_type = typename iterator_traits::difference_type;
+    using value_type = typename It::value_type::second_type;
+    using pointer = const value_type *;
+    using reference = const value_type &;
+    using difference_type = std::ptrdiff_t;
     using iterator_category = std::forward_iterator_tag;
 
-    dense_hash_set_local_iterator() ENTT_NOEXCEPT = default;
+    constexpr dense_set_local_iterator() noexcept
+        : it{},
+          offset{} {}
 
-    dense_hash_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
+    constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
         : it{iter},
           offset{pos} {}
 
-    template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
-    dense_hash_set_local_iterator(const dense_hash_set_local_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
+    template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+    constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
         : it{other.it},
           offset{other.offset} {}
 
-    dense_hash_set_local_iterator &operator++() ENTT_NOEXCEPT {
-        return offset = it[offset].next, *this;
+    constexpr dense_set_local_iterator &operator++() noexcept {
+        return offset = it[offset].first, *this;
     }
 
-    dense_hash_set_local_iterator operator++(int) ENTT_NOEXCEPT {
-        dense_hash_set_local_iterator orig = *this;
+    constexpr dense_set_local_iterator operator++(int) noexcept {
+        dense_set_local_iterator orig = *this;
         return ++(*this), orig;
     }
 
-    [[nodiscard]] pointer operator->() const {
-        return std::addressof(it[offset].element);
+    [[nodiscard]] constexpr pointer operator->() const noexcept {
+        return std::addressof(it[offset].second);
     }
 
-    [[nodiscard]] reference operator*() const {
+    [[nodiscard]] constexpr reference operator*() const noexcept {
         return *operator->();
     }
 
-    [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr std::size_t index() const noexcept {
         return offset;
     }
 
@@ -206,13 +195,13 @@ private:
     std::size_t offset;
 };
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] bool operator==(const dense_hash_set_local_iterator<ILhs> &lhs, const dense_hash_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
     return lhs.index() == rhs.index();
 }
 
-template<typename ILhs, typename IRhs>
-[[nodiscard]] bool operator!=(const dense_hash_set_local_iterator<ILhs> &lhs, const dense_hash_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
     return !(lhs == rhs);
 }
 
@@ -236,27 +225,26 @@ template<typename ILhs, typename IRhs>
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
 template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
-class dense_hash_set final {
+class dense_set {
     static constexpr float default_threshold = 0.875f;
     static constexpr std::size_t minimum_capacity = 8u;
 
-    using allocator_traits = std::allocator_traits<Allocator>;
-    using alloc = typename allocator_traits::template rebind_alloc<Type>;
-    using alloc_traits = typename std::allocator_traits<alloc>;
-
-    using node_type = internal::dense_hash_set_node<Type>;
+    using node_type = std::pair<std::size_t, Type>;
+    using alloc_traits = std::allocator_traits<Allocator>;
+    static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
     using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
     using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
 
-    [[nodiscard]] std::size_t hash_to_bucket(const std::size_t hash) const ENTT_NOEXCEPT {
-        return fast_mod(hash, bucket_count());
+    template<typename Other>
+    [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept {
+        return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count());
     }
 
     template<typename Other>
     [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
         for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
             if(packed.second()(*it, value)) {
-                return begin() + it.index();
+                return begin() + static_cast<typename iterator::difference_type>(it.index());
             }
         }
 
@@ -267,61 +255,45 @@ class dense_hash_set final {
     [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
         for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
             if(packed.second()(*it, value)) {
-                return cbegin() + it.index();
+                return cbegin() + static_cast<typename iterator::difference_type>(it.index());
             }
         }
 
         return cend();
     }
 
-    template<typename Arg>
-    [[nodiscard]] auto get_or_emplace(Arg &&arg) {
-        const auto hash = sparse.second()(arg);
-        auto index = hash_to_bucket(hash);
+    template<typename Other>
+    [[nodiscard]] auto insert_or_do_nothing(Other &&value) {
+        const auto index = value_to_bucket(value);
 
-        if(auto it = constrained_find(arg, index); it != end()) {
+        if(auto it = constrained_find(value, index); it != end()) {
             return std::make_pair(it, false);
         }
 
-        if(const auto count = size() + 1u; count > (bucket_count() * max_load_factor())) {
-            rehash(bucket_count() * 2u);
-            index = hash_to_bucket(hash);
-        }
-
-        packed.first().emplace_back(sparse.first()[index], std::forward<Arg>(arg));
-        // update goes after emplace to enforce exception guarantees
-        sparse.first()[index] = size() - 1u;
+        packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value));
+        sparse.first()[index] = packed.first().size() - 1u;
+        rehash_if_required();
 
         return std::make_pair(--end(), true);
     }
 
-    template<typename Other>
-    bool do_erase(const Other &value) {
-        for(size_type *curr = sparse.first().data() + bucket(value); *curr != std::numeric_limits<size_type>::max(); curr = &packed.first()[*curr].next) {
-            if(packed.second()(packed.first()[*curr].element, value)) {
-                const auto index = *curr;
-                *curr = packed.first()[*curr].next;
-                move_and_pop(index);
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     void move_and_pop(const std::size_t pos) {
         if(const auto last = size() - 1u; pos != last) {
-            size_type *curr = sparse.first().data() + bucket(packed.first().back().element);
-            for(; *curr != last; curr = &packed.first()[*curr].next) {}
-            *curr = pos;
-
-            // basic exception guarantees when value type has a throwing move constructor
+            size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
             packed.first()[pos] = std::move(packed.first().back());
+            for(; *curr != last; curr = &packed.first()[*curr].first) {}
+            *curr = pos;
         }
 
         packed.first().pop_back();
     }
 
+    void rehash_if_required() {
+        if(size() > (bucket_count() * max_load_factor())) {
+            rehash(bucket_count() * 2u);
+        }
+    }
+
 public:
     /*! @brief Key type of the container. */
     using key_type = Type;
@@ -336,179 +308,194 @@ public:
     /*! @brief Allocator type. */
     using allocator_type = Allocator;
     /*! @brief Random access iterator type. */
-    using iterator = internal::dense_hash_set_iterator<typename packed_container_type::pointer>;
+    using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
     /*! @brief Constant random access iterator type. */
-    using const_iterator = internal::dense_hash_set_iterator<typename packed_container_type::const_pointer>;
+    using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
+    /*! @brief Reverse iterator type. */
+    using reverse_iterator = std::reverse_iterator<iterator>;
+    /*! @brief Constant reverse iterator type. */
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
     /*! @brief Forward iterator type. */
-    using local_iterator = internal::dense_hash_set_local_iterator<typename packed_container_type::pointer>;
+    using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
     /*! @brief Constant forward iterator type. */
-    using const_local_iterator = internal::dense_hash_set_local_iterator<typename packed_container_type::const_pointer>;
+    using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
 
     /*! @brief Default constructor. */
-    dense_hash_set()
-        : dense_hash_set(minimum_capacity) {}
+    dense_set()
+        : dense_set{minimum_capacity} {}
 
     /**
      * @brief Constructs an empty container with a given allocator.
      * @param allocator The allocator to use.
      */
-    explicit dense_hash_set(const allocator_type &allocator)
-        : dense_hash_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+    explicit dense_set(const allocator_type &allocator)
+        : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
 
     /**
      * @brief Constructs an empty container with a given allocator and user
      * supplied minimal number of buckets.
-     * @param bucket_count Minimal number of buckets.
+     * @param cnt Minimal number of buckets.
      * @param allocator The allocator to use.
      */
-    dense_hash_set(const size_type bucket_count, const allocator_type &allocator)
-        : dense_hash_set{bucket_count, hasher{}, key_equal{}, allocator} {}
+    dense_set(const size_type cnt, const allocator_type &allocator)
+        : dense_set{cnt, hasher{}, key_equal{}, allocator} {}
 
     /**
      * @brief Constructs an empty container with a given allocator, hash
      * function and user supplied minimal number of buckets.
-     * @param bucket_count Minimal number of buckets.
+     * @param cnt Minimal number of buckets.
      * @param hash Hash function to use.
      * @param allocator The allocator to use.
      */
-    dense_hash_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
-        : dense_hash_set{bucket_count, hash, key_equal{}, allocator} {}
+    dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
+        : dense_set{cnt, hash, key_equal{}, allocator} {}
 
     /**
      * @brief Constructs an empty container with a given allocator, hash
      * function, compare function and user supplied minimal number of buckets.
-     * @param bucket_count Minimal number of buckets.
+     * @param cnt Minimal number of buckets.
      * @param hash Hash function to use.
      * @param equal Compare function to use.
      * @param allocator The allocator to use.
      */
-    explicit dense_hash_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type())
+    explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
         : sparse{allocator, hash},
           packed{allocator, equal},
           threshold{default_threshold} {
-        rehash(bucket_count);
+        rehash(cnt);
     }
 
-    /**
-     * @brief Copy constructor.
-     * @param other The instance to copy from.
-     */
-    dense_hash_set(const dense_hash_set &other)
-        : dense_hash_set{other, alloc_traits::select_on_container_copy_construction(other.get_allocator())} {}
+    /*! @brief Default copy constructor. */
+    dense_set(const dense_set &) = default;
 
     /**
      * @brief Allocator-extended copy constructor.
      * @param other The instance to copy from.
      * @param allocator The allocator to use.
      */
-    dense_hash_set(const dense_hash_set &other, const allocator_type &allocator)
-        : sparse{sparse_container_type{other.sparse.first(), allocator}, other.sparse.second()},
-          // cannot copy the container directly due to a nasty issue of apple clang :(
-          packed{packed_container_type{other.packed.first().begin(), other.packed.first().end(), allocator}, other.packed.second()},
-          threshold{other.threshold} {
-    }
+    dense_set(const dense_set &other, const allocator_type &allocator)
+        : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+          packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+          threshold{other.threshold} {}
 
-    /**
-     * @brief Default move constructor.
-     * @param other The instance to move from.
-     */
-    dense_hash_set(dense_hash_set &&other) ENTT_NOEXCEPT = default;
+    /*! @brief Default move constructor. */
+    dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
 
     /**
      * @brief Allocator-extended move constructor.
      * @param other The instance to move from.
      * @param allocator The allocator to use.
      */
-    dense_hash_set(dense_hash_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT
-        : sparse{sparse_container_type{std::move(other.sparse.first()), allocator}, std::move(other.sparse.second())},
-          // cannot move the container directly due to a nasty issue of apple clang :(
-          packed{packed_container_type{std::make_move_iterator(other.packed.first().begin()), std::make_move_iterator(other.packed.first().end()), allocator}, std::move(other.packed.second())},
+    dense_set(dense_set &&other, const allocator_type &allocator)
+        : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+          packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
           threshold{other.threshold} {}
 
-    /*! @brief Default destructor. */
-    ~dense_hash_set() = default;
-
     /**
-     * @brief Copy assignment operator.
-     * @param other The instance to copy from.
+     * @brief Default copy assignment operator.
      * @return This container.
      */
-    dense_hash_set &operator=(const dense_hash_set &other) {
-        threshold = other.threshold;
-        sparse.first().clear();
-        packed.first().clear();
-        rehash(other.bucket_count());
-        packed.first().reserve(other.packed.first().size());
-        insert(other.cbegin(), other.cend());
-        return *this;
-    }
+    dense_set &operator=(const dense_set &) = default;
 
     /**
      * @brief Default move assignment operator.
-     * @param other The instance to move from.
      * @return This container.
      */
-    dense_hash_set &operator=(dense_hash_set &&other) ENTT_NOEXCEPT = default;
+    dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
 
     /**
      * @brief Returns the associated allocator.
      * @return The associated allocator.
      */
-    [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
         return sparse.first().get_allocator();
     }
 
     /**
      * @brief Returns an iterator to the beginning.
      *
-     * The returned iterator points to the first instance of the internal array.
      * If the array is empty, the returned iterator will be equal to `end()`.
      *
      * @return An iterator to the first instance of the internal array.
      */
-    [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
-        return packed.first().data();
+    [[nodiscard]] const_iterator cbegin() const noexcept {
+        return packed.first().begin();
     }
 
     /*! @copydoc cbegin */
-    [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+    [[nodiscard]] const_iterator begin() const noexcept {
         return cbegin();
     }
 
     /*! @copydoc begin */
-    [[nodiscard]] iterator begin() ENTT_NOEXCEPT {
-        return packed.first().data();
+    [[nodiscard]] iterator begin() noexcept {
+        return packed.first().begin();
     }
 
     /**
      * @brief Returns an iterator to the end.
-     *
-     * The returned iterator points to the element following the last instance
-     * of the internal array. Attempting to dereference the returned iterator
-     * results in undefined behavior.
-     *
      * @return An iterator to the element following the last instance of the
      * internal array.
      */
-    [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
-        return packed.first().data() + size();
+    [[nodiscard]] const_iterator cend() const noexcept {
+        return packed.first().end();
     }
 
     /*! @copydoc cend */
-    [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+    [[nodiscard]] const_iterator end() const noexcept {
         return cend();
     }
 
     /*! @copydoc end */
-    [[nodiscard]] iterator end() ENTT_NOEXCEPT {
-        return packed.first().data() + size();
+    [[nodiscard]] iterator end() noexcept {
+        return packed.first().end();
+    }
+
+    /**
+     * @brief Returns a reverse iterator to the beginning.
+     *
+     * If the array is empty, the returned iterator will be equal to `rend()`.
+     *
+     * @return An iterator to the first instance of the reversed internal array.
+     */
+    [[nodiscard]] const_reverse_iterator crbegin() const noexcept {
+        return std::make_reverse_iterator(cend());
+    }
+
+    /*! @copydoc crbegin */
+    [[nodiscard]] const_reverse_iterator rbegin() const noexcept {
+        return crbegin();
+    }
+
+    /*! @copydoc rbegin */
+    [[nodiscard]] reverse_iterator rbegin() noexcept {
+        return std::make_reverse_iterator(end());
+    }
+
+    /**
+     * @brief Returns a reverse iterator to the end.
+     * @return An iterator to the element following the last instance of the
+     * reversed internal array.
+     */
+    [[nodiscard]] const_reverse_iterator crend() const noexcept {
+        return std::make_reverse_iterator(cbegin());
+    }
+
+    /*! @copydoc crend */
+    [[nodiscard]] const_reverse_iterator rend() const noexcept {
+        return crend();
+    }
+
+    /*! @copydoc rend */
+    [[nodiscard]] reverse_iterator rend() noexcept {
+        return std::make_reverse_iterator(begin());
     }
 
     /**
      * @brief Checks whether a container is empty.
      * @return True if the container is empty, false otherwise.
      */
-    [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+    [[nodiscard]] bool empty() const noexcept {
         return packed.first().empty();
     }
 
@@ -516,12 +503,20 @@ public:
      * @brief Returns the number of elements in a container.
      * @return Number of elements in a container.
      */
-    [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+    [[nodiscard]] size_type size() const noexcept {
         return packed.first().size();
     }
 
+    /**
+     * @brief Returns the maximum possible number of elements.
+     * @return Maximum possible number of elements.
+     */
+    [[nodiscard]] size_type max_size() const noexcept {
+        return packed.first().max_size();
+    }
+
     /*! @brief Clears the container. */
-    void clear() ENTT_NOEXCEPT {
+    void clear() noexcept {
         sparse.first().clear();
         packed.first().clear();
         rehash(0u);
@@ -535,12 +530,12 @@ public:
      * insertion took place.
      */
     std::pair<iterator, bool> insert(const value_type &value) {
-        return emplace(value);
+        return insert_or_do_nothing(value);
     }
 
     /*! @copydoc insert */
     std::pair<iterator, bool> insert(value_type &&value) {
-        return emplace(std::move(value));
+        return insert_or_do_nothing(std::move(value));
     }
 
     /**
@@ -552,12 +547,16 @@ public:
     template<typename It>
     void insert(It first, It last) {
         for(; first != last; ++first) {
-            emplace(*first);
+            insert(*first);
         }
     }
 
     /**
      * @brief Constructs an element in-place, if it does not exist.
+     *
+     * The element is also constructed when the container already has the key,
+     * in which case the newly constructed object is destroyed immediately.
+     *
      * @tparam Args Types of arguments to forward to the constructor of the
      * element.
      * @param args Arguments to forward to the constructor of the element.
@@ -567,10 +566,21 @@ public:
      */
     template<typename... Args>
     std::pair<iterator, bool> emplace(Args &&...args) {
-        if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_const_t<std::remove_reference_t<Args>>, value_type>)) {
-            return get_or_emplace(std::forward<Args>(args)...);
+        if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::decay_t<Args>, value_type>)) {
+            return insert_or_do_nothing(std::forward<Args>(args)...);
         } else {
-            return get_or_emplace(value_type{std::forward<Args>(args)...});
+            auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...));
+            const auto index = value_to_bucket(node.second);
+
+            if(auto it = constrained_find(node.second, index); it != end()) {
+                packed.first().pop_back();
+                return std::make_pair(it, false);
+            }
+
+            std::swap(node.first, sparse.first()[index]);
+            rehash_if_required();
+
+            return std::make_pair(--end(), true);
         }
     }
 
@@ -580,9 +590,9 @@ public:
      * @return An iterator following the removed element.
      */
     iterator erase(const_iterator pos) {
-        const auto dist = std::distance(cbegin(), pos);
+        const auto diff = pos - cbegin();
         erase(*pos);
-        return begin() + dist;
+        return begin() + diff;
     }
 
     /**
@@ -592,13 +602,13 @@ public:
      * @return An iterator following the last removed element.
      */
     iterator erase(const_iterator first, const_iterator last) {
-        const auto dist = std::distance(cbegin(), first);
+        const auto dist = first - cbegin();
 
-        for(auto rfirst = std::make_reverse_iterator(last), rlast = std::make_reverse_iterator(first); rfirst != rlast; ++rfirst) {
-            erase(*rfirst);
+        for(auto from = last - cbegin(); from != dist; --from) {
+            erase(packed.first()[from - 1u].second);
         }
 
-        return dist > static_cast<decltype(dist)>(size()) ? end() : (begin() + dist);
+        return (begin() + dist);
     }
 
     /**
@@ -607,20 +617,50 @@ public:
      * @return Number of elements removed (either 0 or 1).
      */
     size_type erase(const value_type &value) {
-        return do_erase(value);
+        for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
+            if(packed.second()(packed.first()[*curr].second, value)) {
+                const auto index = *curr;
+                *curr = packed.first()[*curr].first;
+                move_and_pop(index);
+                return 1u;
+            }
+        }
+
+        return 0u;
     }
 
     /**
      * @brief Exchanges the contents with those of a given container.
      * @param other Container to exchange the content with.
      */
-    void swap(dense_hash_set &other) {
+    void swap(dense_set &other) {
         using std::swap;
         swap(sparse, other.sparse);
         swap(packed, other.packed);
         swap(threshold, other.threshold);
     }
 
+    /**
+     * @brief Returns the number of elements matching a value (either 1 or 0).
+     * @param key Key value of an element to search for.
+     * @return Number of elements matching the key (either 1 or 0).
+     */
+    [[nodiscard]] size_type count(const value_type &key) const {
+        return find(key) != end();
+    }
+
+    /**
+     * @brief Returns the number of elements matching a key (either 1 or 0).
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return Number of elements matching the key (either 1 or 0).
+     */
+    template<typename Other>
+    [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>>
+    count(const Other &key) const {
+        return find(key) != end();
+    }
+
     /**
      * @brief Finds an element with a given value.
      * @param value Value of an element to search for.
@@ -628,12 +668,12 @@ public:
      * element is found, a past-the-end iterator is returned.
      */
     [[nodiscard]] iterator find(const value_type &value) {
-        return constrained_find(value, bucket(value));
+        return constrained_find(value, value_to_bucket(value));
     }
 
     /*! @copydoc find */
     [[nodiscard]] const_iterator find(const value_type &value) const {
-        return constrained_find(value, bucket(value));
+        return constrained_find(value, value_to_bucket(value));
     }
 
     /**
@@ -646,14 +686,54 @@ public:
     template<typename Other>
     [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
     find(const Other &value) {
-        return constrained_find(value, bucket(value));
+        return constrained_find(value, value_to_bucket(value));
     }
 
     /*! @copydoc find */
     template<typename Other>
     [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
     find(const Other &value) const {
-        return constrained_find(value, bucket(value));
+        return constrained_find(value, value_to_bucket(value));
+    }
+
+    /**
+     * @brief Returns a range containing all elements with a given value.
+     * @param value Value of an element to search for.
+     * @return A pair of iterators pointing to the first element and past the
+     * last element of the range.
+     */
+    [[nodiscard]] std::pair<iterator, iterator> equal_range(const value_type &value) {
+        const auto it = find(value);
+        return {it, it + !(it == end())};
+    }
+
+    /*! @copydoc equal_range */
+    [[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const value_type &value) const {
+        const auto it = find(value);
+        return {it, it + !(it == cend())};
+    }
+
+    /**
+     * @brief Returns a range containing all elements that compare _equivalent_
+     * to a given value.
+     * @tparam Other Type of an element to search for.
+     * @param value Value of an element to search for.
+     * @return A pair of iterators pointing to the first element and past the
+     * last element of the range.
+     */
+    template<typename Other>
+    [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>>
+    equal_range(const Other &value) {
+        const auto it = find(value);
+        return {it, it + !(it == end())};
+    }
+
+    /*! @copydoc equal_range */
+    template<typename Other>
+    [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
+    equal_range(const Other &value) const {
+        const auto it = find(value);
+        return {it, it + !(it == cend())};
     }
 
     /**
@@ -684,7 +764,7 @@ public:
      * @return An iterator to the beginning of the given bucket.
      */
     [[nodiscard]] const_local_iterator cbegin(const size_type index) const {
-        return {packed.first().data(), sparse.first()[index]};
+        return {packed.first().begin(), sparse.first()[index]};
     }
 
     /**
@@ -702,7 +782,7 @@ public:
      * @return An iterator to the beginning of the given bucket.
      */
     [[nodiscard]] local_iterator begin(const size_type index) {
-        return {packed.first().data(), sparse.first()[index]};
+        return {packed.first().begin(), sparse.first()[index]};
     }
 
     /**
@@ -711,7 +791,7 @@ public:
      * @return An iterator to the end of the given bucket.
      */
     [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
-        return {packed.first().data(), std::numeric_limits<size_type>::max()};
+        return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
     }
 
     /**
@@ -719,7 +799,7 @@ public:
      * @param index An index of a bucket to access.
      * @return An iterator to the end of the given bucket.
      */
-    [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
+    [[nodiscard]] const_local_iterator end(const size_type index) const {
         return cend(index);
     }
 
@@ -729,7 +809,7 @@ public:
      * @return An iterator to the end of the given bucket.
      */
     [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
-        return {packed.first().data(), std::numeric_limits<size_type>::max()};
+        return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
     }
 
     /**
@@ -763,7 +843,7 @@ public:
      * @return The bucket for the given element.
      */
     [[nodiscard]] size_type bucket(const value_type &value) const {
-        return hash_to_bucket(sparse.second()(value));
+        return value_to_bucket(value);
     }
 
     /**
@@ -795,19 +875,23 @@ public:
     /**
      * @brief Reserves at least the specified number of buckets and regenerates
      * the hash table.
-     * @param count New number of buckets.
+     * @param cnt New number of buckets.
      */
-    void rehash(const size_type count) {
-        auto value = (std::max)(count, minimum_capacity);
-        value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
+    void rehash(const size_type cnt) {
+        auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
+        const auto cap = static_cast<size_type>(size() / max_load_factor());
+        value = value > cap ? value : cap;
 
         if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
             sparse.first().resize(sz);
-            std::fill(sparse.first().begin(), sparse.first().end(), std::numeric_limits<size_type>::max());
+
+            for(auto &&elem: sparse.first()) {
+                elem = (std::numeric_limits<size_type>::max)();
+            }
 
             for(size_type pos{}, last = size(); pos < last; ++pos) {
-                const auto index = bucket(packed.first()[pos].element);
-                packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
+                const auto index = value_to_bucket(packed.first()[pos].second);
+                packed.first()[pos].first = std::exchange(sparse.first()[index], pos);
             }
         }
     }
@@ -815,11 +899,11 @@ public:
     /**
      * @brief Reserves space for at least the specified number of elements and
      * regenerates the hash table.
-     * @param count New number of elements.
+     * @param cnt New number of elements.
      */
-    void reserve(const size_type count) {
-        packed.first().reserve(count);
-        rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
+    void reserve(const size_type cnt) {
+        packed.first().reserve(cnt);
+        rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor())));
     }
 
     /**

+ 5 - 3
Dependencies/include/entt/container/fwd.hpp

@@ -3,22 +3,24 @@
 
 #include <functional>
 #include <memory>
+#include <utility>
 
 namespace entt {
 
 template<
-    typename Key, typename Type,
+    typename Key,
+    typename Type,
     typename = std::hash<Key>,
     typename = std::equal_to<Key>,
     typename = std::allocator<std::pair<const Key, Type>>>
-class dense_hash_map;
+class dense_map;
 
 template<
     typename Type,
     typename = std::hash<Type>,
     typename = std::equal_to<Type>,
     typename = std::allocator<Type>>
-class dense_hash_set;
+class dense_set;
 
 } // namespace entt
 

+ 4 - 3
Dependencies/include/entt/core/algorithm.hpp

@@ -95,14 +95,15 @@ struct radix_sort {
     template<typename It, typename Getter = identity>
     void operator()(It first, It last, Getter getter = Getter{}) const {
         if(first < last) {
-            static constexpr auto mask = (1 << Bit) - 1;
-            static constexpr auto buckets = 1 << Bit;
-            static constexpr auto passes = N / Bit;
+            constexpr auto passes = N / Bit;
 
             using value_type = typename std::iterator_traits<It>::value_type;
             std::vector<value_type> aux(std::distance(first, last));
 
             auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
+                constexpr auto mask = (1 << Bit) - 1;
+                constexpr auto buckets = 1 << Bit;
+
                 std::size_t index[buckets]{};
                 std::size_t count[buckets]{};
 

+ 138 - 111
Dependencies/include/entt/core/any.hpp

@@ -13,6 +13,40 @@
 
 namespace entt {
 
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+enum class any_operation : std::uint8_t {
+    copy,
+    move,
+    transfer,
+    assign,
+    destroy,
+    compare,
+    get
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Possible modes of an any object. */
+enum class any_policy : std::uint8_t {
+    /*! @brief Default mode, the object owns the contained element. */
+    owner,
+    /*! @brief Aliasing mode, the object _points_ to a non-const element. */
+    ref,
+    /*! @brief Const aliasing mode, the object _points_ to a const element. */
+    cref
+};
+
 /**
  * @brief A SBO friendly, type-safe container for single values of any type.
  * @tparam Len Size of the storage reserved for the small buffer optimization.
@@ -20,82 +54,70 @@ namespace entt {
  */
 template<std::size_t Len, std::size_t Align>
 class basic_any {
-    enum class operation : std::uint8_t {
-        copy,
-        move,
-        transfer,
-        assign,
-        destroy,
-        compare,
-        get
-    };
+    using operation = internal::any_operation;
+    using vtable_type = const void *(const operation, const basic_any &, const void *);
 
-    enum class policy : std::uint8_t {
-        owner,
-        ref,
-        cref
+    struct storage_type {
+        alignas(Align) std::byte data[Len + !Len];
     };
 
-    using storage_type = std::aligned_storage_t<Len + !Len, Align>;
-    using vtable_type = const void *(const operation, const basic_any &, const void *);
-
     template<typename Type>
-    static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
+    static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
 
     template<typename Type>
-    static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
-        static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_reference_t<std::remove_const_t<Type>>, Type>, "Invalid type");
-        const Type *instance = nullptr;
+    static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
+        static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
+        const Type *element = nullptr;
 
         if constexpr(in_situ<Type>) {
-            instance = (value.mode == policy::owner) ? ENTT_LAUNDER(reinterpret_cast<const Type *>(&value.storage)) : static_cast<const Type *>(value.instance);
+            element = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
         } else {
-            instance = static_cast<const Type *>(value.instance);
+            element = static_cast<const Type *>(value.instance);
         }
 
         switch(op) {
         case operation::copy:
             if constexpr(std::is_copy_constructible_v<Type>) {
-                static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*instance);
+                static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
             }
             break;
         case operation::move:
             if constexpr(in_situ<Type>) {
-                if(value.mode == policy::owner) {
-                    return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(instance))};
+                if(value.mode == any_policy::owner) {
+                    return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
                 }
             }
 
             return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
         case operation::transfer:
             if constexpr(std::is_move_assignable_v<Type>) {
-                *const_cast<Type *>(instance) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
+                *const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
                 return other;
             }
             [[fallthrough]];
         case operation::assign:
             if constexpr(std::is_copy_assignable_v<Type>) {
-                *const_cast<Type *>(instance) = *static_cast<const Type *>(other);
+                *const_cast<Type *>(element) = *static_cast<const Type *>(other);
                 return other;
             }
             break;
         case operation::destroy:
             if constexpr(in_situ<Type>) {
-                instance->~Type();
+                element->~Type();
             } else if constexpr(std::is_array_v<Type>) {
-                delete[] instance;
+                delete[] element;
             } else {
-                delete instance;
+                delete element;
             }
             break;
         case operation::compare:
             if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
-                return *static_cast<const Type *>(instance) == *static_cast<const Type *>(other) ? other : nullptr;
+                return *element == *static_cast<const Type *>(other) ? other : nullptr;
             } else {
-                return (instance == other) ? other : nullptr;
+                return (element == other) ? other : nullptr;
             }
         case operation::get:
-            return instance;
+            return element;
         }
 
         return nullptr;
@@ -103,31 +125,32 @@ class basic_any {
 
     template<typename Type, typename... Args>
     void initialize([[maybe_unused]] Args &&...args) {
+        info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+
         if constexpr(!std::is_void_v<Type>) {
-            vtable = basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>;
-            info = &type_id<Type>();
+            vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
 
             if constexpr(std::is_lvalue_reference_v<Type>) {
-                static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
-                mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
+                static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
+                mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
                 instance = (std::addressof(args), ...);
-            } else if constexpr(in_situ<Type>) {
-                if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
-                    new(&storage) Type{std::forward<Args>(args)...};
+            } else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+                if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
+                    new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
                 } else {
-                    new(&storage) Type(std::forward<Args>(args)...);
+                    new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
                 }
             } else {
-                if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
-                    instance = new Type{std::forward<Args>(args)...};
+                if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
+                    instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
                 } else {
-                    instance = new Type(std::forward<Args>(args)...);
+                    instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
                 }
             }
         }
     }
 
-    basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
+    basic_any(const basic_any &other, const any_policy pol) noexcept
         : instance{other.data()},
           info{other.info},
           vtable{other.vtable},
@@ -140,11 +163,8 @@ public:
     static constexpr auto alignment = Align;
 
     /*! @brief Default constructor. */
-    basic_any() ENTT_NOEXCEPT
-        : instance{},
-          info{&type_id<void>()},
-          vtable{},
-          mode{policy::owner} {}
+    constexpr basic_any() noexcept
+        : basic_any{std::in_place_type<void>} {}
 
     /**
      * @brief Constructs a wrapper by directly initializing the new object.
@@ -154,7 +174,10 @@ public:
      */
     template<typename Type, typename... Args>
     explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
-        : basic_any{} {
+        : instance{},
+          info{},
+          vtable{},
+          mode{any_policy::owner} {
         initialize<Type>(std::forward<Args>(args)...);
     }
 
@@ -165,9 +188,7 @@ public:
      */
     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
     basic_any(Type &&value)
-        : basic_any{} {
-        initialize<std::decay_t<Type>>(std::forward<Type>(value));
-    }
+        : basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
 
     /**
      * @brief Copy constructor.
@@ -184,7 +205,7 @@ public:
      * @brief Move constructor.
      * @param other The instance to move from.
      */
-    basic_any(basic_any &&other) ENTT_NOEXCEPT
+    basic_any(basic_any &&other) noexcept
         : instance{},
           info{other.info},
           vtable{other.vtable},
@@ -196,7 +217,7 @@ public:
 
     /*! @brief Frees the internal storage, whatever it means. */
     ~basic_any() {
-        if(vtable && mode == policy::owner) {
+        if(vtable && (mode == any_policy::owner)) {
             vtable(operation::destroy, *this, nullptr);
         }
     }
@@ -221,7 +242,7 @@ public:
      * @param other The instance to move from.
      * @return This any object.
      */
-    basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
+    basic_any &operator=(basic_any &&other) noexcept {
         reset();
 
         if(other.vtable) {
@@ -251,7 +272,7 @@ public:
      * @brief Returns the object type if any, `type_id<void>()` otherwise.
      * @return The object type if any, `type_id<void>()` otherwise.
      */
-    [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
+    [[nodiscard]] const type_info &type() const noexcept {
         return *info;
     }
 
@@ -259,7 +280,7 @@ public:
      * @brief Returns an opaque pointer to the contained instance.
      * @return An opaque pointer the contained instance, if any.
      */
-    [[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+    [[nodiscard]] const void *data() const noexcept {
         return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
     }
 
@@ -268,7 +289,7 @@ public:
      * @param req Expected type.
      * @return An opaque pointer the contained instance, if any.
      */
-    [[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
+    [[nodiscard]] const void *data(const type_info &req) const noexcept {
         return *info == req ? data() : nullptr;
     }
 
@@ -276,8 +297,8 @@ public:
      * @brief Returns an opaque pointer to the contained instance.
      * @return An opaque pointer the contained instance, if any.
      */
-    [[nodiscard]] void *data() ENTT_NOEXCEPT {
-        return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
+    [[nodiscard]] void *data() noexcept {
+        return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
     }
 
     /**
@@ -285,8 +306,8 @@ public:
      * @param req Expected type.
      * @return An opaque pointer the contained instance, if any.
      */
-    [[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
-        return *info == req ? data() : nullptr;
+    [[nodiscard]] void *data(const type_info &req) noexcept {
+        return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
     }
 
     /**
@@ -302,25 +323,21 @@ public:
     }
 
     /**
-     * @brief Copy assigns a value to the contained object without replacing it.
+     * @brief Assigns a value to the contained object without replacing it.
      * @param other The value to assign to the contained object.
      * @return True in case of success, false otherwise.
      */
-    bool assign(const any &other) {
-        if(vtable && mode != policy::cref && *info == *other.info) {
+    bool assign(const basic_any &other) {
+        if(vtable && mode != any_policy::cref && *info == *other.info) {
             return (vtable(operation::assign, *this, other.data()) != nullptr);
         }
 
         return false;
     }
 
-    /**
-     * @brief Move assigns a value to the contained object without replacing it.
-     * @param other The value to assign to the contained object.
-     * @return True in case of success, false otherwise.
-     */
-    bool assign(any &&other) {
-        if(vtable && mode != policy::cref && *info == *other.info) {
+    /*! @copydoc assign */
+    bool assign(basic_any &&other) {
+        if(vtable && mode != any_policy::cref && *info == *other.info) {
             if(auto *val = other.data(); val) {
                 return (vtable(operation::transfer, *this, val) != nullptr);
             } else {
@@ -333,20 +350,22 @@ public:
 
     /*! @brief Destroys contained object */
     void reset() {
-        if(vtable && mode == policy::owner) {
+        if(vtable && (mode == any_policy::owner)) {
             vtable(operation::destroy, *this, nullptr);
         }
 
+        // unnecessary but it helps to detect nasty bugs
+        ENTT_ASSERT((instance = nullptr) == nullptr, "");
         info = &type_id<void>();
         vtable = nullptr;
-        mode = policy::owner;
+        mode = any_policy::owner;
     }
 
     /**
      * @brief Returns false if a wrapper is empty, true otherwise.
      * @return False if the wrapper is empty, true otherwise.
      */
-    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+    [[nodiscard]] explicit operator bool() const noexcept {
         return vtable != nullptr;
     }
 
@@ -355,7 +374,7 @@ public:
      * @param other Wrapper with which to compare.
      * @return False if the two objects differ in their content, true otherwise.
      */
-    bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
+    [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
         if(vtable && *info == *other.info) {
             return (vtable(operation::compare, *this, other.data()) != nullptr);
         }
@@ -363,25 +382,42 @@ public:
         return (!vtable && !other.vtable);
     }
 
+    /**
+     * @brief Checks if two wrappers differ in their content.
+     * @param other Wrapper with which to compare.
+     * @return True if the two objects differ in their content, false otherwise.
+     */
+    [[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
+        return !(*this == other);
+    }
+
     /**
      * @brief Aliasing constructor.
      * @return A wrapper that shares a reference to an unmanaged object.
      */
-    [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
-        return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
+    [[nodiscard]] basic_any as_ref() noexcept {
+        return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
     }
 
     /*! @copydoc as_ref */
-    [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
-        return basic_any{*this, policy::cref};
+    [[nodiscard]] basic_any as_ref() const noexcept {
+        return basic_any{*this, any_policy::cref};
     }
 
     /**
      * @brief Returns true if a wrapper owns its object, false otherwise.
      * @return True if the wrapper owns its object, false otherwise.
      */
-    [[nodiscard]] bool owner() const ENTT_NOEXCEPT {
-        return (mode == policy::owner);
+    [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
+        return (mode == any_policy::owner);
+    }
+
+    /**
+     * @brief Returns the current mode of an any object.
+     * @return The current mode of the any object.
+     */
+    [[nodiscard]] any_policy policy() const noexcept {
+        return mode;
     }
 
 private:
@@ -391,22 +427,9 @@ private:
     };
     const type_info *info;
     vtable_type *vtable;
-    policy mode;
+    any_policy mode;
 };
 
-/**
- * @brief Checks if two wrappers differ in their content.
- * @tparam Len Size of the storage reserved for the small buffer optimization.
- * @tparam Align Alignment requirement.
- * @param lhs A wrapper, either empty or not.
- * @param rhs A wrapper, either empty or not.
- * @return True if the two wrappers differ in their content, false otherwise.
- */
-template<std::size_t Len, std::size_t Align>
-[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
-    return !(lhs == rhs);
-}
-
 /**
  * @brief Performs type-safe access to the contained object.
  * @tparam Type Type to which conversion is required.
@@ -416,7 +439,7 @@ template<std::size_t Len, std::size_t Align>
  * @return The element converted to the requested type.
  */
 template<typename Type, std::size_t Len, std::size_t Align>
-Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
     const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
     ENTT_ASSERT(instance, "Invalid instance");
     return static_cast<Type>(*instance);
@@ -424,7 +447,7 @@ Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
 
 /*! @copydoc any_cast */
 template<typename Type, std::size_t Len, std::size_t Align>
-Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
     // forces const on non-reference types to make them work also with wrappers for const references
     auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
     ENTT_ASSERT(instance, "Invalid instance");
@@ -433,8 +456,8 @@ Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
 
 /*! @copydoc any_cast */
 template<typename Type, std::size_t Len, std::size_t Align>
-Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
-    if constexpr(std::is_copy_constructible_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
+[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
+    if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
         if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
             return static_cast<Type>(std::move(*instance));
         } else {
@@ -449,17 +472,21 @@ Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
 
 /*! @copydoc any_cast */
 template<typename Type, std::size_t Len, std::size_t Align>
-const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
-    const auto &info = type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
+[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
+    const auto &info = type_id<std::remove_cv_t<Type>>();
     return static_cast<const Type *>(data->data(info));
 }
 
 /*! @copydoc any_cast */
 template<typename Type, std::size_t Len, std::size_t Align>
-Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
-    const auto &info = type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
-    // last attempt to make wrappers for const references return their values
-    return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info));
+[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
+    if constexpr(std::is_const_v<Type>) {
+        // last attempt to make wrappers for const references return their values
+        return any_cast<Type>(&std::as_const(*data));
+    } else {
+        const auto &info = type_id<std::remove_cv_t<Type>>();
+        return static_cast<Type *>(data->data(info));
+    }
 }
 
 /**
@@ -472,7 +499,7 @@ Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
  * @return A properly initialized wrapper for an object of the given type.
  */
 template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
-basic_any<Len, Align> make_any(Args &&...args) {
+[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
     return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
 }
 
@@ -485,8 +512,8 @@ basic_any<Len, Align> make_any(Args &&...args) {
  * @return A properly initialized and not necessarily owning wrapper.
  */
 template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
-basic_any<Len, Align> forward_as_any(Type &&value) {
-    return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
+[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
+    return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
 }
 
 } // namespace entt

+ 42 - 32
Dependencies/include/entt/core/compressed_pair.hpp

@@ -5,7 +5,6 @@
 #include <tuple>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
 #include "type_traits.hpp"
 
 namespace entt {
@@ -23,22 +22,22 @@ struct compressed_pair_element {
     using const_reference = const Type &;
 
     template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
-    compressed_pair_element()
+    constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
         : value{} {}
 
-    template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
-    compressed_pair_element(Args &&args)
-        : value{std::forward<Args>(args)} {}
+    template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
+    constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
+        : value{std::forward<Arg>(arg)} {}
 
     template<typename... Args, std::size_t... Index>
-    compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
-        : value{std::get<Index>(args)...} {}
+    constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<Type, Args...>)
+        : value{std::forward<Args>(std::get<Index>(args))...} {}
 
-    [[nodiscard]] reference get() ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr reference get() noexcept {
         return value;
     }
 
-    [[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr const_reference get() const noexcept {
         return value;
     }
 
@@ -53,22 +52,22 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
     using base_type = Type;
 
     template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
-    compressed_pair_element()
+    constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
         : base_type{} {}
 
-    template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
-    compressed_pair_element(Args &&args)
-        : base_type{std::forward<Args>(args)} {}
+    template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
+    constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<base_type, Arg>)
+        : base_type{std::forward<Arg>(arg)} {}
 
     template<typename... Args, std::size_t... Index>
-    compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
-        : base_type{std::get<Index>(args)...} {}
+    constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<base_type, Args...>)
+        : base_type{std::forward<Args>(std::get<Index>(args))...} {}
 
-    [[nodiscard]] reference get() ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr reference get() noexcept {
         return *this;
     }
 
-    [[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr const_reference get() const noexcept {
         return *this;
     }
 };
@@ -111,7 +110,7 @@ public:
      * @tparam Dummy Dummy template parameter used for internal purposes.
      */
     template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
-    constexpr compressed_pair()
+    constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> &&std::is_nothrow_default_constructible_v<second_base>)
         : first_base{},
           second_base{} {}
 
@@ -119,13 +118,13 @@ public:
      * @brief Copy constructor.
      * @param other The instance to copy from.
      */
-    constexpr compressed_pair(const compressed_pair &other) = default;
+    constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v<first_base> &&std::is_nothrow_copy_constructible_v<second_base>) = default;
 
     /**
      * @brief Move constructor.
      * @param other The instance to move from.
      */
-    constexpr compressed_pair(compressed_pair &&other) ENTT_NOEXCEPT = default;
+    constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v<first_base> &&std::is_nothrow_move_constructible_v<second_base>) = default;
 
     /**
      * @brief Constructs a pair from its values.
@@ -135,7 +134,7 @@ public:
      * @param other Value to use to initialize the second element.
      */
     template<typename Arg, typename Other>
-    constexpr compressed_pair(Arg &&arg, Other &&other)
+    constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> &&std::is_nothrow_constructible_v<second_base, Other>)
         : first_base{std::forward<Arg>(arg)},
           second_base{std::forward<Other>(other)} {}
 
@@ -147,7 +146,7 @@ public:
      * @param other Arguments to use to initialize the second element.
      */
     template<typename... Args, typename... Other>
-    constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+    constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> &&std::is_nothrow_constructible_v<second_base, Other...>)
         : first_base{std::move(args), std::index_sequence_for<Args...>{}},
           second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
 
@@ -156,25 +155,25 @@ public:
      * @param other The instance to copy from.
      * @return This compressed pair object.
      */
-    constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+    constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v<first_base> &&std::is_nothrow_copy_assignable_v<second_base>) = default;
 
     /**
      * @brief Move assignment operator.
      * @param other The instance to move from.
      * @return This compressed pair object.
      */
-    constexpr compressed_pair &operator=(compressed_pair &&other) ENTT_NOEXCEPT = default;
+    constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> &&std::is_nothrow_move_assignable_v<second_base>) = default;
 
     /**
      * @brief Returns the first element that a pair stores.
      * @return The first element that a pair stores.
      */
-    [[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr first_type &first() noexcept {
         return static_cast<first_base &>(*this).get();
     }
 
     /*! @copydoc first */
-    [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr const first_type &first() const noexcept {
         return static_cast<const first_base &>(*this).get();
     }
 
@@ -182,12 +181,12 @@ public:
      * @brief Returns the second element that a pair stores.
      * @return The second element that a pair stores.
      */
-    [[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr second_type &second() noexcept {
         return static_cast<second_base &>(*this).get();
     }
 
     /*! @copydoc second */
-    [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr const second_type &second() const noexcept {
         return static_cast<const second_base &>(*this).get();
     }
 
@@ -195,7 +194,7 @@ public:
      * @brief Swaps two compressed pair objects.
      * @param other The compressed pair to swap with.
      */
-    void swap(compressed_pair &other) {
+    constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v<first_type> &&std::is_nothrow_swappable_v<second_type>) {
         using std::swap;
         swap(first(), other.first());
         swap(second(), other.second());
@@ -208,7 +207,7 @@ public:
      * reference to the second element if `Index` is 1.
      */
     template<std::size_t Index>
-    decltype(auto) get() ENTT_NOEXCEPT {
+    constexpr decltype(auto) get() noexcept {
         if constexpr(Index == 0u) {
             return first();
         } else {
@@ -219,7 +218,7 @@ public:
 
     /*! @copydoc get */
     template<std::size_t Index>
-    decltype(auto) get() const ENTT_NOEXCEPT {
+    constexpr decltype(auto) get() const noexcept {
         if constexpr(Index == 0u) {
             return first();
         } else {
@@ -245,7 +244,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::d
  * @param rhs A valid compressed pair object.
  */
 template<typename First, typename Second>
-inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
     lhs.swap(rhs);
 }
 
@@ -255,9 +254,20 @@ inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Sec
 #if !defined __clang_major__ || __clang_major__ > 6
 namespace std {
 
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
 template<typename First, typename Second>
 struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
 
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
 template<size_t Index, typename First, typename Second>
 struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
     static_assert(Index < 2u, "Index out of bounds");

+ 23 - 38
Dependencies/include/entt/core/enum.hpp

@@ -1,8 +1,7 @@
-#ifndef ENTT_CORE_FLAG_HPP
-#define ENTT_CORE_FLAG_HPP
+#ifndef ENTT_CORE_ENUM_HPP
+#define ENTT_CORE_ENUM_HPP
 
 #include <type_traits>
-#include "../config/config.h"
 
 namespace entt {
 
@@ -15,7 +14,7 @@ struct enum_as_bitmask: std::false_type {};
 
 /*! @copydoc enum_as_bitmask */
 template<typename Type>
-struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::true_type {};
+struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {};
 
 /**
  * @brief Helper variable template.
@@ -35,23 +34,23 @@ inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
  * two values provided.
  */
 template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
-operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
-    return Type{static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs)};
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator|(const Type lhs, const Type rhs) noexcept {
+    return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs));
 }
 
 /*! @copydoc operator| */
 template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
-operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
-    return Type{static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs)};
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator&(const Type lhs, const Type rhs) noexcept {
+    return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs));
 }
 
 /*! @copydoc operator| */
 template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
-operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
-    return Type{static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs)};
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator^(const Type lhs, const Type rhs) noexcept {
+    return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
 }
 
 /**
@@ -62,51 +61,37 @@ operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
  * value provided.
  */
 template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
-operator~(const Type value) ENTT_NOEXCEPT {
-    return Type{~static_cast<std::underlying_type_t<Type>>(value)};
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator~(const Type value) noexcept {
+    return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value));
 }
 
 /*! @copydoc operator~ */
 template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, bool>
-operator!(const Type value) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool>
+operator!(const Type value) noexcept {
     return !static_cast<std::underlying_type_t<Type>>(value);
 }
 
 /*! @copydoc operator| */
 template<typename Type>
-constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type &>
-operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator|=(Type &lhs, const Type rhs) noexcept {
     return (lhs = (lhs | rhs));
 }
 
 /*! @copydoc operator| */
 template<typename Type>
-constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type &>
-operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator&=(Type &lhs, const Type rhs) noexcept {
     return (lhs = (lhs & rhs));
 }
 
 /*! @copydoc operator| */
 template<typename Type>
-constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type &>
-operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator^=(Type &lhs, const Type rhs) noexcept {
     return (lhs = (lhs ^ rhs));
 }
 
-/*! @copydoc operator| */
-template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
-operator==(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
-    return (static_cast<std::underlying_type_t<Type>>(lhs) == static_cast<std::underlying_type_t<Type>>(rhs));
-}
-
-/*! @copydoc operator| */
-template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
-operator!=(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
-    return !(lhs == rhs);
-}
-
 #endif

+ 2 - 2
Dependencies/include/entt/core/family.hpp

@@ -19,12 +19,12 @@ class family {
 
 public:
     /*! @brief Unsigned integer type. */
-    using family_type = id_type;
+    using value_type = id_type;
 
     /*! @brief Statically generated unique identifier for the given type. */
     template<typename... Type>
     // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
-    inline static const family_type type = identifier++;
+    inline static const value_type value = identifier++;
 };
 
 } // namespace entt

+ 2 - 2
Dependencies/include/entt/core/fwd.hpp

@@ -1,12 +1,12 @@
 #ifndef ENTT_CORE_FWD_HPP
 #define ENTT_CORE_FWD_HPP
 
-#include <type_traits>
+#include <cstddef>
 #include "../config/config.h"
 
 namespace entt {
 
-template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
 class basic_any;
 
 /*! @brief Alias declaration for type identifiers. */

+ 100 - 80
Dependencies/include/entt/core/hashed_string.hpp

@@ -3,7 +3,6 @@
 
 #include <cstddef>
 #include <cstdint>
-#include "../config/config.h"
 #include "fwd.hpp"
 
 namespace entt {
@@ -32,6 +31,17 @@ struct fnv1a_traits<std::uint64_t> {
     static constexpr std::uint64_t prime = 1099511628211ull;
 };
 
+template<typename Char>
+struct basic_hashed_string {
+    using value_type = Char;
+    using size_type = std::size_t;
+    using hash_type = id_type;
+
+    const value_type *repr;
+    size_type length;
+    hash_type hash;
+};
+
 } // namespace internal
 
 /**
@@ -43,7 +53,7 @@ struct fnv1a_traits<std::uint64_t> {
  * @brief Zero overhead unique identifier.
  *
  * A hashed string is a compile-time tool that allows users to use
- * human-readable identifers in the codebase while using their numeric
+ * human-readable identifiers in the codebase while using their numeric
  * counterparts at runtime.<br/>
  * Because of that, a hashed string can also be used in constant expressions if
  * required.
@@ -55,62 +65,67 @@ struct fnv1a_traits<std::uint64_t> {
  * @tparam Char Character type.
  */
 template<typename Char>
-class basic_hashed_string {
-    using hs_traits = internal::fnv1a_traits<id_type>;
+class basic_hashed_string: internal::basic_hashed_string<Char> {
+    using base_type = internal::basic_hashed_string<Char>;
+    using traits_type = internal::fnv1a_traits<id_type>;
 
     struct const_wrapper {
         // non-explicit constructor on purpose
-        constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {}
-        const Char *str;
+        constexpr const_wrapper(const Char *str) noexcept
+            : repr{str} {}
+
+        const Char *repr;
     };
 
     // Fowler–Noll–Vo hash function v. 1a - the good
-    [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT {
-        auto value = hs_traits::offset;
+    [[nodiscard]] static constexpr auto helper(const Char *str) noexcept {
+        base_type base{str, 0u, traits_type::offset};
 
-        while(*curr != 0) {
-            value = (value ^ static_cast<hs_traits::type>(*(curr++))) * hs_traits::prime;
+        for(; str[base.length]; ++base.length) {
+            base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
         }
 
-        return value;
+        return base;
+    }
+
+    // Fowler–Noll–Vo hash function v. 1a - the good
+    [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept {
+        base_type base{str, len, traits_type::offset};
+
+        for(size_type pos{}; pos < len; ++pos) {
+            base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
+        }
+
+        return base;
     }
 
 public:
     /*! @brief Character type. */
-    using value_type = Char;
+    using value_type = typename base_type::value_type;
     /*! @brief Unsigned integer type. */
-    using hash_type = id_type;
+    using size_type = typename base_type::size_type;
+    /*! @brief Unsigned integer type. */
+    using hash_type = typename base_type::hash_type;
 
     /**
      * @brief Returns directly the numeric representation of a string view.
-     * @param str Human-readable identifer.
-     * @param size Length of the string to hash.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
      * @return The numeric representation of the string.
      */
-    [[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
-        id_type partial{hs_traits::offset};
-        while(size--) { partial = (partial ^ (str++)[0]) * hs_traits::prime; }
-        return partial;
+    [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept {
+        return basic_hashed_string{str, len};
     }
 
     /**
      * @brief Returns directly the numeric representation of a string.
-     *
-     * Forcing template resolution avoids implicit conversions. An
-     * human-readable identifier can be anything but a plain, old bunch of
-     * characters.<br/>
-     * Example of use:
-     * @code{.cpp}
-     * const auto value = basic_hashed_string<char>::value("my.png");
-     * @endcode
-     *
      * @tparam N Number of characters of the identifier.
-     * @param str Human-readable identifer.
+     * @param str Human-readable identifier.
      * @return The numeric representation of the string.
      */
     template<std::size_t N>
-    [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
-        return helper(str);
+    [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept {
+        return basic_hashed_string{str};
     }
 
     /**
@@ -118,33 +133,30 @@ public:
      * @param wrapper Helps achieving the purpose by relying on overloading.
      * @return The numeric representation of the string.
      */
-    [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
-        return helper(wrapper.str);
+    [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept {
+        return basic_hashed_string{wrapper};
     }
 
     /*! @brief Constructs an empty hashed string. */
-    constexpr basic_hashed_string() ENTT_NOEXCEPT
-        : str{nullptr},
-          hash{} {}
+    constexpr basic_hashed_string() noexcept
+        : base_type{} {}
+
+    /**
+     * @brief Constructs a hashed string from a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     */
+    constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept
+        : base_type{helper(str, len)} {}
 
     /**
      * @brief Constructs a hashed string from an array of const characters.
-     *
-     * Forcing template resolution avoids implicit conversions. An
-     * human-readable identifier can be anything but a plain, old bunch of
-     * characters.<br/>
-     * Example of use:
-     * @code{.cpp}
-     * basic_hashed_string<char> hs{"my.png"};
-     * @endcode
-     *
      * @tparam N Number of characters of the identifier.
-     * @param curr Human-readable identifer.
+     * @param str Human-readable identifier.
      */
     template<std::size_t N>
-    constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
-        : str{curr},
-          hash{helper(curr)} {}
+    constexpr basic_hashed_string(const value_type (&str)[N]) noexcept
+        : base_type{helper(str)} {}
 
     /**
      * @brief Explicit constructor on purpose to avoid constructing a hashed
@@ -155,53 +167,61 @@ public:
      *
      * @param wrapper Helps achieving the purpose by relying on overloading.
      */
-    explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
-        : str{wrapper.str},
-          hash{helper(wrapper.str)} {}
+    explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept
+        : base_type{helper(wrapper.repr)} {}
+
+    /**
+     * @brief Returns the size a hashed string.
+     * @return The size of the hashed string.
+     */
+    [[nodiscard]] constexpr size_type size() const noexcept {
+        return base_type::length;
+    }
 
     /**
      * @brief Returns the human-readable representation of a hashed string.
-     * @return The string used to initialize the instance.
+     * @return The string used to initialize the hashed string.
      */
-    [[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
-        return str;
+    [[nodiscard]] constexpr const value_type *data() const noexcept {
+        return base_type::repr;
     }
 
     /**
      * @brief Returns the numeric representation of a hashed string.
-     * @return The numeric representation of the instance.
+     * @return The numeric representation of the hashed string.
      */
-    [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
-        return hash;
+    [[nodiscard]] constexpr hash_type value() const noexcept {
+        return base_type::hash;
     }
 
     /*! @copydoc data */
-    [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr operator const value_type *() const noexcept {
         return data();
     }
 
     /**
      * @brief Returns the numeric representation of a hashed string.
-     * @return The numeric representation of the instance.
+     * @return The numeric representation of the hashed string.
      */
-    [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr operator hash_type() const noexcept {
         return value();
     }
-
-private:
-    const value_type *str;
-    hash_type hash;
 };
 
 /**
  * @brief Deduction guide.
- *
- * It allows to deduce the character type of the hashed string directly from a
- * human-readable identifer provided to the constructor.
- *
+ * @tparam Char Character type.
+ * @param str Human-readable identifier.
+ * @param len Length of the string to hash.
+ */
+template<typename Char>
+basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Deduction guide.
  * @tparam Char Character type.
  * @tparam N Number of characters of the identifier.
- * @param str Human-readable identifer.
+ * @param str Human-readable identifier.
  */
 template<typename Char, std::size_t N>
 basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
@@ -214,7 +234,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
  * @return True if the two hashed strings are identical, false otherwise.
  */
 template<typename Char>
-[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
     return lhs.value() == rhs.value();
 }
 
@@ -226,7 +246,7 @@ template<typename Char>
  * @return True if the two hashed strings differ, false otherwise.
  */
 template<typename Char>
-[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
     return !(lhs == rhs);
 }
 
@@ -238,7 +258,7 @@ template<typename Char>
  * @return True if the first element is less than the second, false otherwise.
  */
 template<typename Char>
-[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
     return lhs.value() < rhs.value();
 }
 
@@ -251,7 +271,7 @@ template<typename Char>
  * otherwise.
  */
 template<typename Char>
-[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
     return !(rhs < lhs);
 }
 
@@ -264,7 +284,7 @@ template<typename Char>
  * otherwise.
  */
 template<typename Char>
-[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
     return rhs < lhs;
 }
 
@@ -277,7 +297,7 @@ template<typename Char>
  * false otherwise.
  */
 template<typename Char>
-[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
     return !(lhs < rhs);
 }
 
@@ -294,8 +314,8 @@ inline namespace literals {
  * @param str The literal without its suffix.
  * @return A properly initialized hashed string.
  */
-[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
-    return entt::hashed_string{str};
+[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept {
+    return hashed_string{str};
 }
 
 /**
@@ -303,8 +323,8 @@ inline namespace literals {
  * @param str The literal without its suffix.
  * @return A properly initialized hashed wstring.
  */
-[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
-    return entt::hashed_wstring{str};
+[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept {
+    return hashed_wstring{str};
 }
 
 } // namespace literals

+ 11 - 35
Dependencies/include/entt/core/ident.hpp

@@ -4,54 +4,30 @@
 #include <cstddef>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
 #include "fwd.hpp"
 #include "type_traits.hpp"
 
 namespace entt {
 
 /**
- * @brief Types identifiers.
- *
- * Variable template used to generate identifiers at compile-time for the given
- * types. Use the `get` member function to know what's the identifier associated
- * to the specific type.
- *
- * @note
- * Identifiers are constant expression and can be used in any context where such
- * an expression is required. As an example:
- * @code{.cpp}
- * using id = entt::identifier<a_type, another_type>;
- *
- * switch(a_type_identifier) {
- * case id::type<a_type>:
- *     // ...
- *     break;
- * case id::type<another_type>:
- *     // ...
- *     break;
- * default:
- *     // ...
- * }
- * @endcode
- *
- * @tparam Types List of types for which to generate identifiers.
+ * @brief Type integral identifiers.
+ * @tparam Type List of types for which to generate identifiers.
  */
-template<typename... Types>
-class identifier {
-    template<typename Type, std::size_t... Index>
-    [[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) {
-        static_assert(std::disjunction_v<std::is_same<Type, Types>...>, "Invalid type");
-        return (0 + ... + (std::is_same_v<Type, type_list_element_t<Index, type_list<std::decay_t<Types>...>>> ? id_type{Index} : id_type{}));
+template<typename... Type>
+class ident {
+    template<typename Curr, std::size_t... Index>
+    [[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) noexcept {
+        static_assert((std::is_same_v<Curr, Type> || ...), "Invalid type");
+        return (0 + ... + (std::is_same_v<Curr, type_list_element_t<Index, type_list<std::decay_t<Type>...>>> ? id_type{Index} : id_type{}));
     }
 
 public:
     /*! @brief Unsigned integer type. */
-    using identifier_type = id_type;
+    using value_type = id_type;
 
     /*! @brief Statically generated unique identifier for the given type. */
-    template<typename Type>
-    static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
+    template<typename Curr>
+    static constexpr value_type value = get<std::decay_t<Curr>>(std::index_sequence_for<Type...>{});
 };
 
 } // namespace entt

+ 115 - 34
Dependencies/include/entt/core/iterator.hpp

@@ -3,8 +3,8 @@
 
 #include <iterator>
 #include <memory>
+#include <type_traits>
 #include <utility>
-#include "../config/config.h"
 
 namespace entt {
 
@@ -14,76 +14,157 @@ namespace entt {
  */
 template<typename Type>
 struct input_iterator_pointer final {
+    /*! @brief Value type. */
+    using value_type = Type;
     /*! @brief Pointer type. */
-    using pointer = decltype(std::addressof(std::declval<Type &>()));
-
-    /*! @brief Default copy constructor, deleted on purpose. */
-    input_iterator_pointer(const input_iterator_pointer &) = delete;
-
-    /*! @brief Default move constructor. */
-    input_iterator_pointer(input_iterator_pointer &&) = default;
+    using pointer = Type *;
+    /*! @brief Reference type. */
+    using reference = Type &;
 
     /**
      * @brief Constructs a proxy object by move.
      * @param val Value to use to initialize the proxy object.
      */
-    input_iterator_pointer(Type &&val)
+    constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v<value_type>)
         : value{std::move(val)} {}
 
     /**
-     * @brief Default copy assignment operator, deleted on purpose.
-     * @return This proxy object.
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
      */
-    input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+    [[nodiscard]] constexpr pointer operator->() noexcept {
+        return std::addressof(value);
+    }
 
     /**
-     * @brief Default move assignment operator.
-     * @return This proxy object.
+     * @brief Dereference operator for accessing wrapped values.
+     * @return A reference to the wrapped value.
      */
-    input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+    [[nodiscard]] constexpr reference operator*() noexcept {
+        return value;
+    }
+
+private:
+    Type value;
+};
+
+/**
+ * @brief Plain iota iterator (waiting for C++20).
+ * @tparam Type Value type.
+ */
+template<typename Type>
+class iota_iterator final {
+    static_assert(std::is_integral_v<Type>, "Not an integral type");
+
+public:
+    /*! @brief Value type, likely an integral one. */
+    using value_type = Type;
+    /*! @brief Invalid pointer type. */
+    using pointer = void;
+    /*! @brief Non-reference type, same as value type. */
+    using reference = value_type;
+    /*! @brief Difference type. */
+    using difference_type = std::ptrdiff_t;
+    /*! @brief Iterator category. */
+    using iterator_category = std::input_iterator_tag;
+
+    /*! @brief Default constructor. */
+    constexpr iota_iterator() noexcept
+        : current{} {}
 
     /**
-     * @brief Access operator for accessing wrapped values.
-     * @return A pointer to the wrapped value.
+     * @brief Constructs an iota iterator from a given value.
+     * @param init The initial value assigned to the iota iterator.
      */
-    [[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
-        return std::addressof(value);
+    constexpr iota_iterator(const value_type init) noexcept
+        : current{init} {}
+
+    /**
+     * @brief Pre-increment operator.
+     * @return This iota iterator.
+     */
+    constexpr iota_iterator &operator++() noexcept {
+        return ++current, *this;
+    }
+
+    /**
+     * @brief Post-increment operator.
+     * @return This iota iterator.
+     */
+    constexpr iota_iterator operator++(int) noexcept {
+        iota_iterator orig = *this;
+        return ++(*this), orig;
+    }
+
+    /**
+     * @brief Dereference operator.
+     * @return The underlying value.
+     */
+    [[nodiscard]] constexpr reference operator*() const noexcept {
+        return current;
     }
 
 private:
-    Type value;
+    value_type current;
 };
 
+/**
+ * @brief Comparison operator.
+ * @tparam Type Value type of the iota iterator.
+ * @param lhs A properly initialized iota iterator.
+ * @param rhs A properly initialized iota iterator.
+ * @return True if the two iterators are identical, false otherwise.
+ */
+template<typename Type>
+[[nodiscard]] constexpr bool operator==(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
+    return *lhs == *rhs;
+}
+
+/**
+ * @brief Comparison operator.
+ * @tparam Type Value type of the iota iterator.
+ * @param lhs A properly initialized iota iterator.
+ * @param rhs A properly initialized iota iterator.
+ * @return True if the two iterators differ, false otherwise.
+ */
+template<typename Type>
+[[nodiscard]] constexpr bool operator!=(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
 /**
  * @brief Utility class to create an iterable object from a pair of iterators.
- * @tparam It Type of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
  */
-template<typename It>
+template<typename It, typename Sentinel = It>
 struct iterable_adaptor final {
-    /*! @brief Type of the objects returned during iteration. */
+    /*! @brief Value type. */
     using value_type = typename std::iterator_traits<It>::value_type;
     /*! @brief Iterator type. */
     using iterator = It;
-    /*! @brief Const iterator type. */
-    using const_iterator = iterator;
+    /*! @brief Sentinel type. */
+    using sentinel = Sentinel;
 
     /*! @brief Default constructor. */
-    iterable_adaptor() = default;
+    constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> &&std::is_nothrow_default_constructible_v<sentinel>)
+        : first{},
+          last{} {}
 
     /**
      * @brief Creates an iterable object from a pair of iterators.
      * @param from Begin iterator.
      * @param to End iterator.
      */
-    iterable_adaptor(It from, It to)
-        : first{from},
-          last{to} {}
+    constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> &&std::is_nothrow_move_constructible_v<sentinel>)
+        : first{std::move(from)},
+          last{std::move(to)} {}
 
     /**
      * @brief Returns an iterator to the beginning.
      * @return An iterator to the first element of the range.
      */
-    [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr iterator begin() const noexcept {
         return first;
     }
 
@@ -92,23 +173,23 @@ struct iterable_adaptor final {
      * @return An iterator to the element following the last element of the
      * range.
      */
-    [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr sentinel end() const noexcept {
         return last;
     }
 
     /*! @copydoc begin */
-    [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr iterator cbegin() const noexcept {
         return begin();
     }
 
     /*! @copydoc end */
-    [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr sentinel cend() const noexcept {
         return end();
     }
 
 private:
     It first;
-    It last;
+    Sentinel last;
 };
 
 } // namespace entt

+ 211 - 28
Dependencies/include/entt/core/memory.hpp

@@ -4,12 +4,51 @@
 #include <cstddef>
 #include <limits>
 #include <memory>
+#include <tuple>
 #include <type_traits>
 #include <utility>
 #include "../config/config.h"
 
 namespace entt {
 
+/**
+ * @brief Checks whether a value is a power of two or not (waiting for C++20 and
+ * `std::has_single_bit`).
+ * @param value A value that may or may not be a power of two.
+ * @return True if the value is a power of two, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept {
+    return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value
+ * (waiting for C++20 and `std::bit_ceil`).
+ * @param value The value to use.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
+    ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
+    std::size_t curr = value - (value != 0u);
+
+    for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
+        curr |= curr >> next;
+    }
+
+    return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @param value A value for which to calculate the modulus.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
+    ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two");
+    return value & (mod - 1u);
+}
+
 /**
  * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
  * @tparam Type Pointer type.
@@ -17,8 +56,8 @@ namespace entt {
  * @return A raw pointer that represents the address of the original pointer.
  */
 template<typename Type>
-[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
-    if constexpr(std::is_pointer_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
+[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept {
+    if constexpr(std::is_pointer_v<std::decay_t<Type>>) {
         return ptr;
     } else {
         return to_address(std::forward<Type>(ptr).operator->());
@@ -32,7 +71,7 @@ template<typename Type>
  * @param rhs Another valid allocator.
  */
 template<typename Allocator>
-constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
     if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
         lhs = rhs;
     }
@@ -45,7 +84,7 @@ constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator
  * @param rhs Another valid allocator.
  */
 template<typename Allocator>
-constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
     if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
         lhs = std::move(rhs);
     }
@@ -58,49 +97,193 @@ constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator
  * @param rhs Another valid allocator.
  */
 template<typename Allocator>
-constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
-    ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
-
+constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
     if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
         using std::swap;
         swap(lhs, rhs);
+    } else {
+        ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers");
     }
 }
 
 /**
- * @brief Checks whether a value is a power of two or not.
- * @param value A value that may or may not be a power of two.
- * @return True if the value is a power of two, false otherwise.
+ * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
+ * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
-    return value && ((value & (value - 1)) == 0);
+template<typename Allocator>
+struct allocation_deleter: private Allocator {
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Pointer type. */
+    using pointer = typename std::allocator_traits<Allocator>::pointer;
+
+    /**
+     * @brief Inherited constructors.
+     * @param alloc The allocator to use.
+     */
+    constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v<allocator_type>)
+        : Allocator{alloc} {}
+
+    /**
+     * @brief Destroys the pointed object and deallocates its memory.
+     * @param ptr A valid pointer to an object of the given type.
+     */
+    constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
+        using alloc_traits = std::allocator_traits<Allocator>;
+        alloc_traits::destroy(*this, to_address(ptr));
+        alloc_traits::deallocate(*this, ptr, 1u);
+    }
+};
+
+/**
+ * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
+ * @tparam Type Type of object to allocate for and to construct.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A properly initialized unique pointer with a custom deleter.
+ */
+template<typename Type, typename Allocator, typename... Args>
+ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
+    static_assert(!std::is_array_v<Type>, "Array types are not supported");
+
+    using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
+    using allocator_type = typename alloc_traits::allocator_type;
+
+    allocator_type alloc{allocator};
+    auto ptr = alloc_traits::allocate(alloc, 1u);
+
+    ENTT_TRY {
+        alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
+    }
+    ENTT_CATCH {
+        alloc_traits::deallocate(alloc, ptr, 1u);
+        ENTT_THROW;
+    }
+
+    return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
 }
 
 /**
- * @brief Computes the smallest power of two greater than or equal to a value.
- * @param value The value to use.
- * @return The smallest power of two greater than or equal to the given value.
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
  */
-[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
-    ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
-    std::size_t curr = value - (value != 0u);
 
-    for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
-        curr |= curr >> next;
+namespace internal {
+
+template<typename Type>
+struct uses_allocator_construction {
+    template<typename Allocator, typename... Params>
+    static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept {
+        if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
+            return std::forward_as_tuple(std::forward<Params>(params)...);
+        } else {
+            static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+            if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
+                return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>{std::allocator_arg, allocator, std::forward<Params>(params)...};
+            } else {
+                static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
+                return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
+            }
+        }
     }
+};
 
-    return ++curr;
+template<typename Type, typename Other>
+struct uses_allocator_construction<std::pair<Type, Other>> {
+    using type = std::pair<Type, Other>;
+
+    template<typename Allocator, typename First, typename Second>
+    static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept {
+        return std::make_tuple(
+            std::piecewise_construct,
+            std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
+            std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
+    }
+
+    template<typename Allocator>
+    static constexpr auto args(const Allocator &allocator) noexcept {
+        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+    }
+
+    template<typename Allocator, typename First, typename Second>
+    static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept {
+        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
+    }
+
+    template<typename Allocator, typename First, typename Second>
+    static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) noexcept {
+        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
+    }
+
+    template<typename Allocator, typename First, typename Second>
+    static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) noexcept {
+        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
+    }
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Prepares the argument list needed to
+ * create an object of a given type by means of uses-allocator construction.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept {
+    return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
 }
 
 /**
- * @brief Fast module utility function (powers of two only).
- * @param value A value for which to calculate the modulus.
- * @param mod _Modulus_, it must be a power of two.
- * @return The common remainder.
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A newly created object of the given type.
  */
-[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
-    ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
-    return value & (mod - 1u);
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+    return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction at an uninitialized memory location.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param value Memory location in which to place the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A pointer to the newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
+    return std::apply([value](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
 }
 
 } // namespace entt

+ 2 - 2
Dependencies/include/entt/core/monostate.hpp

@@ -25,7 +25,7 @@ struct monostate {
      * @param val User data to assign to the given key.
      */
     template<typename Type>
-    void operator=(Type val) const ENTT_NOEXCEPT {
+    void operator=(Type val) const noexcept {
         value<Type> = val;
     }
 
@@ -35,7 +35,7 @@ struct monostate {
      * @return Stored value, if any.
      */
     template<typename Type>
-    operator Type() const ENTT_NOEXCEPT {
+    operator Type() const noexcept {
         return value<Type>;
     }
 

+ 76 - 2
Dependencies/include/entt/core/tuple.hpp

@@ -4,10 +4,44 @@
 #include <tuple>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
 
 namespace entt {
 
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename>
+struct is_tuple_impl: std::false_type {};
+
+template<typename... Args>
+struct is_tuple_impl<std::tuple<Args...>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is a
+ * tuple, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+struct is_tuple: internal::is_tuple_impl<std::remove_cv_t<Type>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_tuple_v = is_tuple<Type>::value;
+
 /**
  * @brief Utility function to unwrap tuples of a single element.
  * @tparam Type Tuple type of any sizes.
@@ -16,7 +50,7 @@ namespace entt {
  * element otherwise.
  */
 template<typename Type>
-constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT {
+constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept {
     if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) {
         return std::get<0>(std::forward<Type>(value));
     } else {
@@ -24,6 +58,46 @@ constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT {
     }
 }
 
+/**
+ * @brief Utility class to forward-and-apply tuple objects.
+ * @tparam Func Type of underlying invocable object.
+ */
+template<typename Func>
+struct forward_apply: private Func {
+    /**
+     * @brief Constructs a forward-and-apply object.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+    template<typename... Args>
+    constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>)
+        : Func{std::forward<Args>(args)...} {}
+
+    /**
+     * @brief Forwards and applies the arguments with the underlying function.
+     * @tparam Type Tuple-like type to forward to the underlying function.
+     * @param args Parameters to forward to the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+    template<typename Type>
+    constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval<Func &>(), args))) {
+        return std::apply(static_cast<Func &>(*this), std::forward<Type>(args));
+    }
+
+    /*! @copydoc operator()() */
+    template<typename Type>
+    constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval<const Func &>(), args))) {
+        return std::apply(static_cast<const Func &>(*this), std::forward<Type>(args));
+    }
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Type of underlying invocable object.
+ */
+template<typename Func>
+forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_cv_t<Func>>>;
+
 } // namespace entt
 
 #endif

+ 41 - 34
Dependencies/include/entt/core/type_info.hpp

@@ -19,14 +19,14 @@ namespace entt {
 namespace internal {
 
 struct ENTT_API type_index final {
-    [[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+    [[nodiscard]] static id_type next() noexcept {
         static ENTT_MAYBE_ATOMIC(id_type) value{};
         return value++;
     }
 };
 
 template<typename Type>
-[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+[[nodiscard]] constexpr auto stripped_type_name() noexcept {
 #if defined ENTT_PRETTY_FUNCTION
     std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
     auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
@@ -38,26 +38,26 @@ template<typename Type>
 }
 
 template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
-[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr std::string_view type_name(int) noexcept {
     constexpr auto value = stripped_type_name<Type>();
     return value;
 }
 
 template<typename Type>
-[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+[[nodiscard]] std::string_view type_name(char) noexcept {
     static const auto value = stripped_type_name<Type>();
     return value;
 }
 
 template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
-[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr id_type type_hash(int) noexcept {
     constexpr auto stripped = stripped_type_name<Type>();
     constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
     return value;
 }
 
 template<typename Type>
-[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+[[nodiscard]] id_type type_hash(char) noexcept {
     static const auto value = [](const auto stripped) {
         return hashed_string::value(stripped.data(), stripped.size());
     }(stripped_type_name<Type>());
@@ -81,13 +81,13 @@ struct ENTT_API type_index final {
      * @brief Returns the sequential identifier of a given type.
      * @return The sequential identifier of a given type.
      */
-    [[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+    [[nodiscard]] static id_type value() noexcept {
         static const id_type value = internal::type_index::next();
         return value;
     }
 
     /*! @copydoc value */
-    [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr operator id_type() const noexcept {
         return value();
     }
 };
@@ -103,16 +103,16 @@ struct type_hash final {
      * @return The numeric representation of the given type.
      */
 #if defined ENTT_PRETTY_FUNCTION
-    [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+    [[nodiscard]] static constexpr id_type value() noexcept {
         return internal::type_hash<Type>(0);
 #else
-    [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+    [[nodiscard]] static constexpr id_type value() noexcept {
         return type_index<Type>::value();
 #endif
     }
 
     /*! @copydoc value */
-    [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr operator id_type() const noexcept {
         return value();
     }
 };
@@ -127,33 +127,33 @@ struct type_name final {
      * @brief Returns the name of a given type.
      * @return The name of the given type.
      */
-    [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+    [[nodiscard]] static constexpr std::string_view value() noexcept {
         return internal::type_name<Type>(0);
     }
 
     /*! @copydoc value */
-    [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr operator std::string_view() const noexcept {
         return value();
     }
 };
 
 /*! @brief Implementation specific information about a type. */
-class type_info final {
-    template<typename Type>
-    friend const type_info &type_id() ENTT_NOEXCEPT;
-
+struct type_info final {
+    /**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
     template<typename Type>
-    constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
-        : seq{type_index<std::remove_reference_t<std::remove_const_t<Type>>>::value()},
-          identifier{type_hash<std::remove_reference_t<std::remove_const_t<Type>>>::value()},
-          alias{type_name<std::remove_reference_t<std::remove_const_t<Type>>>::value()} {}
+    constexpr type_info(std::in_place_type_t<Type>) noexcept
+        : seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+          identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+          alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
 
-public:
     /**
      * @brief Type index.
      * @return Type index.
      */
-    [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr id_type index() const noexcept {
         return seq;
     }
 
@@ -161,7 +161,7 @@ public:
      * @brief Type hash.
      * @return Type hash.
      */
-    [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr id_type hash() const noexcept {
         return identifier;
     }
 
@@ -169,7 +169,7 @@ public:
      * @brief Type name.
      * @return Type name.
      */
-    [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr std::string_view name() const noexcept {
         return alias;
     }
 
@@ -185,7 +185,7 @@ private:
  * @param rhs A type info object.
  * @return True if the two type info objects are identical, false otherwise.
  */
-[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept {
     return lhs.hash() == rhs.hash();
 }
 
@@ -195,7 +195,7 @@ private:
  * @param rhs A type info object.
  * @return True if the two type info objects differ, false otherwise.
  */
-[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept {
     return !(lhs == rhs);
 }
 
@@ -205,7 +205,7 @@ private:
  * @param rhs A valid type info object.
  * @return True if the first element is less than the second, false otherwise.
  */
-[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
     return lhs.index() < rhs.index();
 }
 
@@ -216,7 +216,7 @@ private:
  * @return True if the first element is less than or equal to the second, false
  * otherwise.
  */
-[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
     return !(rhs < lhs);
 }
 
@@ -227,7 +227,7 @@ private:
  * @return True if the first element is greater than the second, false
  * otherwise.
  */
-[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
     return rhs < lhs;
 }
 
@@ -238,30 +238,37 @@ private:
  * @return True if the first element is greater than or equal to the second,
  * false otherwise.
  */
-[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
     return !(lhs < rhs);
 }
 
 /**
  * @brief Returns the type info object associated to a given type.
  *
+ * The returned element refers to an object with static storage duration.<br/>
  * The type doesn't need to be a complete type. If the type is a reference, the
  * result refers to the referenced type. In all cases, top-level cv-qualifiers
  * are ignored.
  *
  * @tparam Type Type for which to generate a type info object.
- * @return A properly initialized type info object.
+ * @return A reference to a properly initialized type info object.
  */
 template<typename Type>
-[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+[[nodiscard]] const type_info &type_id() noexcept {
     if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
-        static type_info instance{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>};
+        static type_info instance{std::in_place_type<Type>};
         return instance;
     } else {
         return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
     }
 }
 
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) noexcept {
+    return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
 } // namespace entt
 
 #endif

+ 322 - 66
Dependencies/include/entt/core/type_traits.hpp

@@ -3,6 +3,7 @@
 
 #include <cstddef>
 #include <iterator>
+#include <tuple>
 #include <type_traits>
 #include <utility>
 #include "../config/config.h"
@@ -16,7 +17,7 @@ namespace entt {
  */
 template<std::size_t N>
 struct choice_t
-    // Unfortunately, doxygen cannot parse such a construct.
+    // unfortunately, doxygen cannot parse such a construct
     : /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
 {};
 
@@ -55,7 +56,6 @@ using type_identity_t = typename type_identity<Type>::type;
 /**
  * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
  * @tparam Type The type of which to return the size.
- * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
  */
 template<typename Type, typename = void>
 struct size_of: std::integral_constant<std::size_t, 0u> {};
@@ -121,22 +121,22 @@ struct type_list_element;
 /**
  * @brief Provides compile-time indexed access to the types of a type list.
  * @tparam Index Index of the type to return.
- * @tparam Type First type provided by the type list.
+ * @tparam First First type provided by the type list.
  * @tparam Other Other types provided by the type list.
  */
-template<std::size_t Index, typename Type, typename... Other>
-struct type_list_element<Index, type_list<Type, Other...>>
+template<std::size_t Index, typename First, typename... Other>
+struct type_list_element<Index, type_list<First, Other...>>
     : type_list_element<Index - 1u, type_list<Other...>> {};
 
 /**
  * @brief Provides compile-time indexed access to the types of a type list.
- * @tparam Type First type provided by the type list.
+ * @tparam First First type provided by the type list.
  * @tparam Other Other types provided by the type list.
  */
-template<typename Type, typename... Other>
-struct type_list_element<0u, type_list<Type, Other...>> {
+template<typename First, typename... Other>
+struct type_list_element<0u, type_list<First, Other...>> {
     /*! @brief Searched type. */
-    using type = Type;
+    using type = First;
 };
 
 /**
@@ -147,6 +147,58 @@ struct type_list_element<0u, type_list<Type, Other...>> {
 template<std::size_t Index, typename List>
 using type_list_element_t = typename type_list_element<Index, List>::type;
 
+/*! @brief Primary template isn't defined on purpose. */
+template<typename, typename>
+struct type_list_index;
+
+/**
+ * @brief Provides compile-time type access to the types of a type list.
+ * @tparam Type Type to look for and for which to return the index.
+ * @tparam First First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename First, typename... Other>
+struct type_list_index<Type, type_list<First, Other...>> {
+    /*! @brief Unsigned integer type. */
+    using value_type = std::size_t;
+    /*! @brief Compile-time position of the given type in the sublist. */
+    static constexpr value_type value = 1u + type_list_index<Type, type_list<Other...>>::value;
+};
+
+/**
+ * @brief Provides compile-time type access to the types of a type list.
+ * @tparam Type Type to look for and for which to return the index.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_index<Type, type_list<Type, Other...>> {
+    static_assert(type_list_index<Type, type_list<Other...>>::value == sizeof...(Other), "Non-unique type");
+    /*! @brief Unsigned integer type. */
+    using value_type = std::size_t;
+    /*! @brief Compile-time position of the given type in the sublist. */
+    static constexpr value_type value = 0u;
+};
+
+/**
+ * @brief Provides compile-time type access to the types of a type list.
+ * @tparam Type Type to look for and for which to return the index.
+ */
+template<typename Type>
+struct type_list_index<Type, type_list<>> {
+    /*! @brief Unsigned integer type. */
+    using value_type = std::size_t;
+    /*! @brief Compile-time position of the given type in the sublist. */
+    static constexpr value_type value = 0u;
+};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for and for which to return the index.
+ */
+template<typename Type, typename List>
+inline constexpr std::size_t type_list_index_v = type_list_index<Type, List>::value;
+
 /**
  * @brief Concatenates multiple type lists.
  * @tparam Type Types provided by the first type list.
@@ -211,7 +263,7 @@ template<typename Type, typename... Other>
 struct type_list_unique<type_list<Type, Other...>> {
     /*! @brief A type list without duplicate types. */
     using type = std::conditional_t<
-        std::disjunction_v<std::is_same<Type, Other>...>,
+        (std::is_same_v<Type, Other> || ...),
         typename type_list_unique<type_list<Other...>>::type,
         type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
 };
@@ -245,7 +297,8 @@ struct type_list_contains;
  * @tparam Other Type to look for.
  */
 template<typename... Type, typename Other>
-struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+struct type_list_contains<type_list<Type...>, Other>
+    : std::bool_constant<(std::is_same_v<Type, Other> || ...)> {};
 
 /**
  * @brief Helper variable template.
@@ -277,6 +330,29 @@ struct type_list_diff<type_list<Type...>, type_list<Other...>> {
 template<typename... List>
 using type_list_diff_t = typename type_list_diff<List...>::type;
 
+/*! @brief Primary template isn't defined on purpose. */
+template<typename, template<typename...> class>
+struct type_list_transform;
+
+/**
+ * @brief Applies a given _function_ to a type list and generate a new list.
+ * @tparam Type Types provided by the type list.
+ * @tparam Op Unary operation as template class with a type member named `type`.
+ */
+template<typename... Type, template<typename...> class Op>
+struct type_list_transform<type_list<Type...>, Op> {
+    /*! @brief Resulting type list after applying the transform function. */
+    using type = type_list<typename Op<Type>::type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type list.
+ * @tparam Op Unary operation as template class with a type member named `type`.
+ */
+template<typename List, template<typename...> class Op>
+using type_list_transform_t = typename type_list_transform<List, Op>::type;
+
 /**
  * @brief A class to use to push around lists of constant values, nothing more.
  * @tparam Value Values provided by the value list.
@@ -310,10 +386,20 @@ struct value_list_element<Index, value_list<Value, Other...>>
  */
 template<auto Value, auto... Other>
 struct value_list_element<0u, value_list<Value, Other...>> {
+    /*! @brief Searched type. */
+    using type = decltype(Value);
     /*! @brief Searched value. */
     static constexpr auto value = Value;
 };
 
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+using value_list_element_t = typename value_list_element<Index, List>::type;
+
 /**
  * @brief Helper type.
  * @tparam Index Index of the value to return.
@@ -322,6 +408,58 @@ struct value_list_element<0u, value_list<Value, Other...>> {
 template<std::size_t Index, typename List>
 inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
 
+/*! @brief Primary template isn't defined on purpose. */
+template<auto, typename>
+struct value_list_index;
+
+/**
+ * @brief Provides compile-time type access to the values of a value list.
+ * @tparam Value Value to look for and for which to return the index.
+ * @tparam First First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto First, auto... Other>
+struct value_list_index<Value, value_list<First, Other...>> {
+    /*! @brief Unsigned integer type. */
+    using value_type = std::size_t;
+    /*! @brief Compile-time position of the given value in the sublist. */
+    static constexpr value_type value = 1u + value_list_index<Value, value_list<Other...>>::value;
+};
+
+/**
+ * @brief Provides compile-time type access to the values of a value list.
+ * @tparam Value Value to look for and for which to return the index.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_index<Value, value_list<Value, Other...>> {
+    static_assert(value_list_index<Value, value_list<Other...>>::value == sizeof...(Other), "Non-unique type");
+    /*! @brief Unsigned integer type. */
+    using value_type = std::size_t;
+    /*! @brief Compile-time position of the given value in the sublist. */
+    static constexpr value_type value = 0u;
+};
+
+/**
+ * @brief Provides compile-time type access to the values of a value list.
+ * @tparam Value Value to look for and for which to return the index.
+ */
+template<auto Value>
+struct value_list_index<Value, value_list<>> {
+    /*! @brief Unsigned integer type. */
+    using value_type = std::size_t;
+    /*! @brief Compile-time position of the given type in the sublist. */
+    static constexpr value_type value = 0u;
+};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Value list.
+ * @tparam Value Value to look for and for which to return the index.
+ */
+template<auto Value, typename List>
+inline constexpr std::size_t value_list_index_v = value_list_index<Value, List>::value;
+
 /**
  * @brief Concatenates multiple value lists.
  * @tparam Value Values provided by the first value list.
@@ -373,6 +511,89 @@ struct value_list_cat<value_list<Value...>> {
 template<typename... List>
 using value_list_cat_t = typename value_list_cat<List...>::type;
 
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct value_list_unique;
+
+/**
+ * @brief Removes duplicates values from a value list.
+ * @tparam Value One of the values provided by the given value list.
+ * @tparam Other The other values provided by the given value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_unique<value_list<Value, Other...>> {
+    /*! @brief A value list without duplicate types. */
+    using type = std::conditional_t<
+        ((Value == Other) || ...),
+        typename value_list_unique<value_list<Other...>>::type,
+        value_list_cat_t<value_list<Value>, typename value_list_unique<value_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates values from a value list. */
+template<>
+struct value_list_unique<value_list<>> {
+    /*! @brief A value list without duplicate types. */
+    using type = value_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A value list.
+ */
+template<typename Type>
+using value_list_unique_t = typename value_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a value list contains
+ * a given value, false otherwise.
+ * @tparam List Value list.
+ * @tparam Value Value to look for.
+ */
+template<typename List, auto Value>
+struct value_list_contains;
+
+/**
+ * @copybrief value_list_contains
+ * @tparam Value Values provided by the value list.
+ * @tparam Other Value to look for.
+ */
+template<auto... Value, auto Other>
+struct value_list_contains<value_list<Value...>, Other>
+    : std::bool_constant<((Value == Other) || ...)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Value list.
+ * @tparam Value Value to look for.
+ */
+template<typename List, auto Value>
+inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+class value_list_diff;
+
+/**
+ * @brief Computes the difference between two value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ */
+template<auto... Value, auto... Other>
+class value_list_diff<value_list<Value...>, value_list<Other...>> {
+    using v141_toolset_workaround = value_list<Other...>;
+
+public:
+    /*! @brief A value list that is the difference between the two value lists. */
+    using type = value_list_cat_t<std::conditional_t<value_list_contains_v<v141_toolset_workaround, Value>, value_list<>, value_list<Value>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists between which to compute the difference.
+ */
+template<typename... List>
+using value_list_diff_t = typename value_list_diff<List...>::type;
+
 /*! @brief Same as std::is_invocable, but with tuples. */
 template<typename, typename>
 struct is_applicable: std::false_type {};
@@ -476,7 +697,7 @@ struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Typ
 
 /*! @copydoc is_iterator */
 template<typename Type>
-struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_cv_t<std::remove_pointer_t<Type>>>>>
     : internal::has_iterator_category<Type> {};
 
 /**
@@ -486,33 +707,6 @@ struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<st
 template<typename Type>
 inline constexpr bool is_iterator_v = is_iterator<Type>::value;
 
-/**
- * @brief Provides the member constant `value` to true if a given type is of the
- * required iterator type, false otherwise.
- * @tparam Type The type to test.
- * @tparam It Required iterator type.
- */
-template<typename Type, typename It, typename = void>
-struct is_iterator_type: std::false_type {};
-
-/*! @copydoc is_iterator_type */
-template<typename Type, typename It>
-struct is_iterator_type<Type, It, std::enable_if_t<is_iterator_v<Type> && std::is_same_v<Type, It>>>
-    : std::true_type {};
-
-/*! @copydoc is_iterator_type */
-template<typename Type, typename It>
-struct is_iterator_type<Type, It, std::enable_if_t<!std::is_same_v<Type, It>, std::void_t<typename It::iterator_type>>>
-    : is_iterator_type<Type, typename It::iterator_type> {};
-
-/**
- * @brief Helper variable template.
- * @tparam Type The type to test.
- * @tparam It Required iterator type.
- */
-template<typename Type, typename It>
-inline constexpr bool is_iterator_type_v = is_iterator_type<Type, It>::value;
-
 /**
  * @brief Provides the member constant `value` to true if a given type is both
  * an empty and non-final class, false otherwise.
@@ -520,7 +714,7 @@ inline constexpr bool is_iterator_type_v = is_iterator_type<Type, It>::value;
  */
 template<typename Type>
 struct is_ebco_eligible
-    : std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+    : std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {};
 
 /**
  * @brief Helper variable template.
@@ -548,14 +742,6 @@ struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::tr
 template<typename Type>
 inline constexpr bool is_transparent_v = is_transparent<Type>::value;
 
-/**
- * @brief Provides the member constant `value` to true if a given type is
- * equality comparable, false otherwise.
- * @tparam Type The type to test.
- */
-template<typename Type, typename = void>
-struct is_equality_comparable: std::false_type {};
-
 /**
  * @cond TURN_OFF_DOXYGEN
  * Internal details not to be documented.
@@ -569,33 +755,52 @@ struct has_tuple_size_value: std::false_type {};
 template<typename Type>
 struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
 
+template<typename, typename = void>
+struct has_value_type: std::false_type {};
+
+template<typename Type>
+struct has_value_type<Type, std::void_t<typename Type::value_type>>: std::true_type {};
+
+template<typename>
+[[nodiscard]] constexpr bool dispatch_is_equality_comparable();
+
 template<typename Type, std::size_t... Index>
 [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
-    return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+    return (dispatch_is_equality_comparable<std::tuple_element_t<Index, Type>>() && ...);
 }
 
 template<typename>
-[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
-    return true;
+[[nodiscard]] constexpr bool maybe_equality_comparable(char) {
+    return false;
 }
 
 template<typename Type>
-[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
-    if constexpr(is_iterator_v<Type>) {
-        return true;
-    } else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
-        return maybe_equality_comparable<Type>(choice<0>);
-    } else {
-        return is_equality_comparable<typename Type::value_type>::value;
-    }
+[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval<Type>() == std::declval<Type>()) {
+    return true;
 }
 
 template<typename Type>
-[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
-    if constexpr(has_tuple_size_value<Type>::value) {
-        return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+[[nodiscard]] constexpr bool dispatch_is_equality_comparable() {
+    if constexpr(std::is_array_v<Type>) {
+        return false;
+    } else if constexpr(is_iterator_v<Type>) {
+        return maybe_equality_comparable<Type>(0);
+    } else if constexpr(has_value_type<Type>::value) {
+        if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+            return maybe_equality_comparable<Type>(0);
+        } else if constexpr(dispatch_is_equality_comparable<typename Type::value_type>()) {
+            return maybe_equality_comparable<Type>(0);
+        } else {
+            return false;
+        }
+    } else if constexpr(is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>) {
+        if constexpr(has_tuple_size_value<Type>::value) {
+            return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+        } else {
+            return maybe_equality_comparable<Type>(0);
+        }
     } else {
-        return maybe_equality_comparable<Type>(choice<1>);
+        return maybe_equality_comparable<Type>(0);
     }
 }
 
@@ -606,10 +811,17 @@ template<typename Type>
  * @endcond
  */
 
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+struct is_equality_comparable: std::bool_constant<internal::dispatch_is_equality_comparable<Type>()> {};
+
 /*! @copydoc is_equality_comparable */
 template<typename Type>
-struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
-    : std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+struct is_equality_comparable<const Type>: is_equality_comparable<Type> {};
 
 /**
  * @brief Helper variable template.
@@ -633,7 +845,7 @@ struct constness_as {
 template<typename To, typename From>
 struct constness_as<To, const From> {
     /*! @brief The type resulting from the transcription of the constness. */
-    using type = std::add_const_t<To>;
+    using type = const To;
 };
 
 /**
@@ -673,6 +885,50 @@ public:
 template<typename Member>
 using member_class_t = typename member_class<Member>::type;
 
+/**
+ * @brief Extracts the n-th argument of a given function or member function.
+ * @tparam Index The index of the argument to extract.
+ * @tparam Candidate A valid function, member function or data member type.
+ */
+template<std::size_t Index, typename Candidate>
+class nth_argument {
+    template<typename Ret, typename... Args>
+    static constexpr type_list<Args...> pick_up(Ret (*)(Args...));
+
+    template<typename Ret, typename Class, typename... Args>
+    static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...));
+
+    template<typename Ret, typename Class, typename... Args>
+    static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...) const);
+
+    template<typename Type, typename Class>
+    static constexpr type_list<Type> pick_up(Type Class ::*);
+
+public:
+    /*! @brief N-th argument of the given function or member function. */
+    using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index The index of the argument to extract.
+ * @tparam Candidate A valid function, member function or data member type.
+ */
+template<std::size_t Index, typename Candidate>
+using nth_argument_t = typename nth_argument<Index, Candidate>::type;
+
 } // namespace entt
 
+template<typename... Type>
+struct std::tuple_size<entt::type_list<Type...>>: std::integral_constant<std::size_t, entt::type_list<Type...>::size> {};
+
+template<std::size_t Index, typename... Type>
+struct std::tuple_element<Index, entt::type_list<Type...>>: entt::type_list_element<Index, entt::type_list<Type...>> {};
+
+template<auto... Value>
+struct std::tuple_size<entt::value_list<Value...>>: std::integral_constant<std::size_t, entt::value_list<Value...>::size> {};
+
+template<std::size_t Index, auto... Value>
+struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_element<Index, entt::value_list<Value...>> {};
+
 #endif

+ 13 - 13
Dependencies/include/entt/core/utility.hpp

@@ -1,8 +1,8 @@
 #ifndef ENTT_CORE_UTILITY_HPP
 #define ENTT_CORE_UTILITY_HPP
 
+#include <type_traits>
 #include <utility>
-#include "../config/config.h"
 
 namespace entt {
 
@@ -17,8 +17,8 @@ struct identity {
      * @param value The actual argument.
      * @return The submitted value as-is.
      */
-    template<class Type>
-    [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+    template<typename Type>
+    [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept {
         return std::forward<Type>(value);
     }
 };
@@ -31,7 +31,7 @@ struct identity {
  * @return Pointer to the member.
  */
 template<typename Type, typename Class>
-[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept {
     return member;
 }
 
@@ -42,7 +42,7 @@ template<typename Type, typename Class>
  * @return Pointer to the function.
  */
 template<typename Func>
-[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr auto overload(Func *func) noexcept {
     return func;
 }
 
@@ -50,7 +50,7 @@ template<typename Func>
  * @brief Helper type for visitors.
  * @tparam Func Types of function objects.
  */
-template<class... Func>
+template<typename... Func>
 struct overloaded: Func... {
     using Func::operator()...;
 };
@@ -59,20 +59,20 @@ struct overloaded: Func... {
  * @brief Deduction guide.
  * @tparam Func Types of function objects.
  */
-template<class... Func>
+template<typename... Func>
 overloaded(Func...) -> overloaded<Func...>;
 
 /**
  * @brief Basic implementation of a y-combinator.
  * @tparam Func Type of a potentially recursive function.
  */
-template<class Func>
+template<typename Func>
 struct y_combinator {
     /**
      * @brief Constructs a y-combinator from a given function.
      * @param recursive A potentially recursive function.
      */
-    y_combinator(Func recursive)
+    constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v<Func>)
         : func{std::move(recursive)} {}
 
     /**
@@ -81,14 +81,14 @@ struct y_combinator {
      * @param args Parameters to use to invoke the underlying function.
      * @return Return value of the underlying function, if any.
      */
-    template<class... Args>
-    decltype(auto) operator()(Args &&...args) const {
+    template<typename... Args>
+    constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v<Func, const y_combinator &, Args...>) {
         return func(*this, std::forward<Args>(args)...);
     }
 
     /*! @copydoc operator()() */
-    template<class... Args>
-    decltype(auto) operator()(Args &&...args) {
+    template<typename... Args>
+    constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v<Func, y_combinator &, Args...>) {
         return func(*this, std::forward<Args>(args)...);
     }
 

+ 45 - 17
Dependencies/include/entt/entity/component.hpp

@@ -1,36 +1,64 @@
 #ifndef ENTT_ENTITY_COMPONENT_HPP
 #define ENTT_ENTITY_COMPONENT_HPP
 
+#include <cstddef>
 #include <type_traits>
 #include "../config/config.h"
+#include "fwd.hpp"
 
 namespace entt {
 
-/*! @brief Commonly used default traits for all types. */
-struct basic_component_traits {
-    /*! @brief Pointer stability, default is `false`. */
-    static constexpr auto in_place_delete = true;
-    /*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */
-    static constexpr auto ignore_if_empty = ENTT_IGNORE_IF_EMPTY;
-    /*! @brief Page size, default is `ENTT_PACKED_PAGE`. */
-    static constexpr auto page_size = ENTT_PACKED_PAGE;
-};
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, typename = void>
+//struct in_place_delete : std::bool_constant<!(std::is_move_constructible_v<Type> &&std::is_move_assignable_v<Type>)> {};
+struct in_place_delete: std::true_type {}; // Modified so in_place_delete defaults to true
+
+template<>
+struct in_place_delete<void>: std::false_type {};
+
+template<typename Type>
+struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
+    : std::true_type {};
+
+template<typename Type, typename = void>
+struct page_size: std::integral_constant<std::size_t, !std::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {};
+
+template<>
+struct page_size<void>: std::integral_constant<std::size_t, 0u> {};
+
+template<typename Type>
+struct page_size<Type, std::void_t<decltype(Type::page_size)>>
+    : std::integral_constant<std::size_t, Type::page_size> {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
 
 /**
  * @brief Common way to access various properties of components.
  * @tparam Type Type of component.
  */
 template<typename Type, typename = void>
-struct component_traits: basic_component_traits {
+struct component_traits {
     static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
-};
 
-/**
- * @brief Helper variable template.
- * @tparam Type Type of component.
- */
-template<class Type>
-inline constexpr bool ignore_as_empty_v = component_traits<Type>::ignore_if_empty &&std::is_empty_v<Type>;
+    /*! @brief Component type. */
+    using type = Type;
+
+    /*! @brief Pointer stability, default is `false`. */
+    static constexpr bool in_place_delete = internal::in_place_delete<Type>::value;
+    /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */
+    static constexpr std::size_t page_size = internal::page_size<Type>::value;
+};
 
 } // namespace entt
 

+ 101 - 56
Dependencies/include/entt/entity/entity.hpp

@@ -16,35 +16,47 @@ namespace entt {
 
 namespace internal {
 
+// waiting for C++20 and std::popcount
+template<typename Type>
+constexpr int popcount(Type value) noexcept {
+    return value ? (int(value & 1) + popcount(value >> 1)) : 0;
+}
+
 template<typename, typename = void>
 struct entt_traits;
 
 template<typename Type>
 struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
-    : entt_traits<std::underlying_type_t<Type>> {};
+    : entt_traits<std::underlying_type_t<Type>> {
+    using value_type = Type;
+};
 
 template<typename Type>
 struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
-    : entt_traits<typename Type::entity_type> {};
+    : entt_traits<typename Type::entity_type> {
+    using value_type = Type;
+};
 
 template<>
 struct entt_traits<std::uint32_t> {
+    using value_type = std::uint32_t;
+
     using entity_type = std::uint32_t;
     using version_type = std::uint16_t;
 
     static constexpr entity_type entity_mask = 0xFFFFF;
     static constexpr entity_type version_mask = 0xFFF;
-    static constexpr std::size_t entity_shift = 20u;
 };
 
 template<>
 struct entt_traits<std::uint64_t> {
+    using value_type = std::uint64_t;
+
     using entity_type = std::uint64_t;
     using version_type = std::uint32_t;
 
     static constexpr entity_type entity_mask = 0xFFFFFFFF;
     static constexpr entity_type version_mask = 0xFFFFFFFF;
-    static constexpr std::size_t entity_shift = 32u;
 };
 
 } // namespace internal
@@ -55,31 +67,35 @@ struct entt_traits<std::uint64_t> {
  */
 
 /**
- * @brief Entity traits.
- * @tparam Type Type of identifier.
+ * @brief Common basic entity traits implementation.
+ * @tparam Traits Actual entity traits to use.
  */
-template<typename Type>
-class entt_traits: internal::entt_traits<Type> {
-    using base_type = internal::entt_traits<Type>;
+template<typename Traits>
+class basic_entt_traits {
+    static constexpr auto length = internal::popcount(Traits::entity_mask);
+
+    static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask");
+    static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask");
 
 public:
     /*! @brief Value type. */
-    using value_type = Type;
+    using value_type = typename Traits::value_type;
     /*! @brief Underlying entity type. */
-    using entity_type = typename base_type::entity_type;
+    using entity_type = typename Traits::entity_type;
     /*! @brief Underlying version type. */
-    using version_type = typename base_type::version_type;
-    /*! @brief Reserved identifier. */
-    static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
-    /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
-    static constexpr auto page_size = ENTT_SPARSE_PAGE;
+    using version_type = typename Traits::version_type;
+
+    /*! @brief Entity mask size. */
+    static constexpr entity_type entity_mask = Traits::entity_mask;
+    /*! @brief Version mask size */
+    static constexpr entity_type version_mask = Traits::version_mask;
 
     /**
      * @brief Converts an entity to its underlying type.
      * @param value The value to convert.
      * @return The integral representation of the given value.
      */
-    [[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT {
+    [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept {
         return static_cast<entity_type>(value);
     }
 
@@ -88,8 +104,8 @@ public:
      * @param value The value to convert.
      * @return The integral representation of the entity part.
      */
-    [[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT {
-        return (to_integral(value) & base_type::entity_mask);
+    [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept {
+        return (to_integral(value) & entity_mask);
     }
 
     /**
@@ -97,8 +113,18 @@ public:
      * @param value The value to convert.
      * @return The integral representation of the version part.
      */
-    [[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT {
-        return (to_integral(value) >> base_type::entity_shift);
+    [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
+        return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
+    }
+
+    /**
+     * @brief Returns the successor of a given identifier.
+     * @param value The identifier of which to return the successor.
+     * @return The successor of the given identifier.
+     */
+    [[nodiscard]] static constexpr value_type next(const value_type value) noexcept {
+        const auto vers = to_version(value) + 1;
+        return construct(to_integral(value), static_cast<version_type>(vers + (vers == version_mask)));
     }
 
     /**
@@ -111,8 +137,8 @@ public:
      * @param version The version part of the identifier.
      * @return A properly constructed identifier.
      */
-    [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) ENTT_NOEXCEPT {
-        return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
+    [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
+        return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
     }
 
     /**
@@ -125,36 +151,53 @@ public:
      * @param rhs The identifier from which to take the version part.
      * @return A properly constructed identifier.
      */
-    [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT {
-        constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
-        return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
+    [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
+        return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
     }
 };
 
 /**
- * @copydoc entt_traits<Entity>::to_integral
+ * @brief Entity traits.
+ * @tparam Type Type of identifier.
+ */
+template<typename Type>
+struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
+    /*! @brief Base type. */
+    using base_type = basic_entt_traits<internal::entt_traits<Type>>;
+    /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
+    static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
+};
+
+/**
+ * @brief Converts an entity to its underlying type.
  * @tparam Entity The value type.
+ * @param value The value to convert.
+ * @return The integral representation of the given value.
  */
 template<typename Entity>
-[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept {
     return entt_traits<Entity>::to_integral(value);
 }
 
 /**
- * @copydoc entt_traits<Entity>::to_entity
+ * @brief Returns the entity part once converted to the underlying type.
  * @tparam Entity The value type.
+ * @param value The value to convert.
+ * @return The integral representation of the entity part.
  */
 template<typename Entity>
-[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept {
     return entt_traits<Entity>::to_entity(value);
 }
 
 /**
- * @copydoc entt_traits<Entity>::to_version
+ * @brief Returns the version part once converted to the underlying type.
  * @tparam Entity The value type.
+ * @param value The value to convert.
+ * @return The integral representation of the version part.
  */
 template<typename Entity>
-[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) noexcept {
     return entt_traits<Entity>::to_version(value);
 }
 
@@ -166,9 +209,10 @@ struct null_t {
      * @return The null representation for the given type.
      */
     template<typename Entity>
-    [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
-        using entity_traits = entt_traits<Entity>;
-        return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
+    [[nodiscard]] constexpr operator Entity() const noexcept {
+        using traits_type = entt_traits<Entity>;
+        constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
+        return value;
     }
 
     /**
@@ -176,7 +220,7 @@ struct null_t {
      * @param other A null object.
      * @return True in all cases.
      */
-    [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept {
         return true;
     }
 
@@ -185,7 +229,7 @@ struct null_t {
      * @param other A null object.
      * @return False in all cases.
      */
-    [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept {
         return false;
     }
 
@@ -196,9 +240,9 @@ struct null_t {
      * @return False if the two elements differ, true otherwise.
      */
     template<typename Entity>
-    [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
-        using entity_traits = entt_traits<Entity>;
-        return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
+    [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
+        using traits_type = entt_traits<Entity>;
+        return traits_type::to_entity(entity) == traits_type::to_entity(*this);
     }
 
     /**
@@ -208,7 +252,7 @@ struct null_t {
      * @return True if the two elements differ, false otherwise.
      */
     template<typename Entity>
-    [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
         return !(entity == *this);
     }
 };
@@ -221,7 +265,7 @@ struct null_t {
  * @return False if the two elements differ, true otherwise.
  */
 template<typename Entity>
-[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept {
     return other.operator==(entity);
 }
 
@@ -233,7 +277,7 @@ template<typename Entity>
  * @return True if the two elements differ, false otherwise.
  */
 template<typename Entity>
-[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept {
     return !(other == entity);
 }
 
@@ -245,9 +289,10 @@ struct tombstone_t {
      * @return The tombstone representation for the given type.
      */
     template<typename Entity>
-    [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
-        using entity_traits = entt_traits<Entity>;
-        return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
+    [[nodiscard]] constexpr operator Entity() const noexcept {
+        using traits_type = entt_traits<Entity>;
+        constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
+        return value;
     }
 
     /**
@@ -255,7 +300,7 @@ struct tombstone_t {
      * @param other A tombstone object.
      * @return True in all cases.
      */
-    [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept {
         return true;
     }
 
@@ -264,7 +309,7 @@ struct tombstone_t {
      * @param other A tombstone object.
      * @return False in all cases.
      */
-    [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept {
         return false;
     }
 
@@ -275,9 +320,9 @@ struct tombstone_t {
      * @return False if the two elements differ, true otherwise.
      */
     template<typename Entity>
-    [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
-        using entity_traits = entt_traits<Entity>;
-        return entity_traits::to_version(entity) == entity_traits::to_version(*this);
+    [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
+        using traits_type = entt_traits<Entity>;
+        return traits_type::to_version(entity) == traits_type::to_version(*this);
     }
 
     /**
@@ -287,7 +332,7 @@ struct tombstone_t {
      * @return True if the two elements differ, false otherwise.
      */
     template<typename Entity>
-    [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
         return !(entity == *this);
     }
 };
@@ -300,7 +345,7 @@ struct tombstone_t {
  * @return False if the two elements differ, true otherwise.
  */
 template<typename Entity>
-[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept {
     return other.operator==(entity);
 }
 
@@ -312,7 +357,7 @@ template<typename Entity>
  * @return True if the two elements differ, false otherwise.
  */
 template<typename Entity>
-[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
+[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept {
     return !(other == entity);
 }
 
@@ -320,7 +365,7 @@ template<typename Entity>
  * @brief Compile-time constant for null entities.
  *
  * There exist implicit conversions from this variable to identifiers of any
- * allowed type. Similarly, there exist comparision operators between the null
+ * allowed type. Similarly, there exist comparison operators between the null
  * entity and any other identifier.
  */
 inline constexpr null_t null{};
@@ -329,7 +374,7 @@ inline constexpr null_t null{};
  * @brief Compile-time constant for tombstone entities.
  *
  * There exist implicit conversions from this variable to identifiers of any
- * allowed type. Similarly, there exist comparision operators between the
+ * allowed type. Similarly, there exist comparison operators between the
  * tombstone entity and any other identifier.
  */
 inline constexpr tombstone_t tombstone{};

+ 184 - 35
Dependencies/include/entt/entity/fwd.hpp

@@ -1,31 +1,49 @@
 #ifndef ENTT_ENTITY_FWD_HPP
 #define ENTT_ENTITY_FWD_HPP
 
+#include <cstdint>
 #include <memory>
+#include <type_traits>
 #include "../core/fwd.hpp"
-#include "utility.hpp"
+#include "../core/type_traits.hpp"
 
 namespace entt {
 
-template<typename Entity, typename = std::allocator<Entity>>
+/*! @brief Default entity identifier. */
+enum class entity : id_type {};
+
+/*! @brief Storage deletion policy. */
+enum class deletion_policy : std::uint8_t {
+    /*! @brief Swap-and-pop deletion policy. */
+    swap_and_pop = 0u,
+    /*! @brief In-place deletion policy. */
+    in_place = 1u,
+    /*! @brief Swap-only deletion policy. */
+    swap_only = 2u
+};
+
+template<typename Entity = entity, typename = std::allocator<Entity>>
 class basic_sparse_set;
 
-template<typename, typename Type, typename = std::allocator<Type>, typename = void>
+template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void>
 class basic_storage;
 
-template<typename>
+template<typename, typename>
+class basic_sigh_mixin;
+
+template<typename Entity = entity, typename = std::allocator<Entity>>
 class basic_registry;
 
-template<typename, typename, typename, typename = void>
+template<typename, typename, typename = void>
 class basic_view;
 
-template<typename>
-struct basic_runtime_view;
+template<typename Type, typename = std::allocator<Type *>>
+class basic_runtime_view;
 
-template<typename, typename, typename, typename>
+template<typename, typename, typename>
 class basic_group;
 
-template<typename>
+template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>>
 class basic_observer;
 
 template<typename>
@@ -43,74 +61,205 @@ class basic_snapshot_loader;
 template<typename>
 class basic_continuous_loader;
 
-/*! @brief Default entity identifier. */
-enum class entity : id_type {};
-
 /*! @brief Alias declaration for the most common use case. */
-using sparse_set = basic_sparse_set<entity>;
+using sparse_set = basic_sparse_set<>;
 
 /**
  * @brief Alias declaration for the most common use case.
- * @tparam Args Other template parameters.
+ * @tparam Type Type of objects assigned to the entities.
  */
-template<typename... Args>
-using storage = basic_storage<entity, Args...>;
+template<typename Type>
+using storage = basic_storage<Type>;
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Type Underlying storage type.
+ */
+template<typename Type>
+using sigh_mixin = basic_sigh_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
 
 /*! @brief Alias declaration for the most common use case. */
-using registry = basic_registry<entity>;
+using registry = basic_registry<>;
 
 /*! @brief Alias declaration for the most common use case. */
-using observer = basic_observer<entity>;
+using observer = basic_observer<registry>;
 
 /*! @brief Alias declaration for the most common use case. */
-using organizer = basic_organizer<entity>;
+using organizer = basic_organizer<registry>;
 
 /*! @brief Alias declaration for the most common use case. */
-using handle = basic_handle<entity>;
+using handle = basic_handle<registry>;
 
 /*! @brief Alias declaration for the most common use case. */
-using const_handle = basic_handle<const entity>;
+using const_handle = basic_handle<const registry>;
 
 /**
  * @brief Alias declaration for the most common use case.
  * @tparam Args Other template parameters.
  */
 template<typename... Args>
-using handle_view = basic_handle<entity, Args...>;
+using handle_view = basic_handle<registry, Args...>;
 
 /**
  * @brief Alias declaration for the most common use case.
  * @tparam Args Other template parameters.
  */
 template<typename... Args>
-using const_handle_view = basic_handle<const entity, Args...>;
+using const_handle_view = basic_handle<const registry, Args...>;
+
+/*! @brief Alias declaration for the most common use case. */
+using snapshot = basic_snapshot<registry>;
 
 /*! @brief Alias declaration for the most common use case. */
-using snapshot = basic_snapshot<entity>;
+using snapshot_loader = basic_snapshot_loader<registry>;
 
 /*! @brief Alias declaration for the most common use case. */
-using snapshot_loader = basic_snapshot_loader<entity>;
+using continuous_loader = basic_continuous_loader<registry>;
 
 /*! @brief Alias declaration for the most common use case. */
-using continuous_loader = basic_continuous_loader<entity>;
+using runtime_view = basic_runtime_view<sparse_set>;
+
+/*! @brief Alias declaration for the most common use case. */
+using const_runtime_view = basic_runtime_view<const sparse_set>;
+
+/**
+ * @brief Alias for exclusion lists.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct exclude_t final: type_list<Type...> {
+    /*! @brief Default constructor. */
+    explicit constexpr exclude_t() {}
+};
+
+/**
+ * @brief Variable template for exclusion lists.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr exclude_t<Type...> exclude{};
+
+/**
+ * @brief Alias for lists of observed components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct get_t final: type_list<Type...> {
+    /*! @brief Default constructor. */
+    explicit constexpr get_t() {}
+};
+
+/**
+ * @brief Variable template for lists of observed components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr get_t<Type...> get{};
+
+/**
+ * @brief Alias for lists of owned components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct owned_t final: type_list<Type...> {
+    /*! @brief Default constructor. */
+    explicit constexpr owned_t() {}
+};
+
+/**
+ * @brief Variable template for lists of owned components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr owned_t<Type...> owned{};
+
+/**
+ * @brief Applies a given _function_ to a get list and generate a new list.
+ * @tparam Type Types provided by the get list.
+ * @tparam Op Unary operation as template class with a type member named `type`.
+ */
+template<typename... Type, template<typename...> class Op>
+struct type_list_transform<get_t<Type...>, Op> {
+    /*! @brief Resulting get list after applying the transform function. */
+    using type = get_t<typename Op<Type>::type...>;
+};
+
+/**
+ * @brief Applies a given _function_ to an exclude list and generate a new list.
+ * @tparam Type Types provided by the exclude list.
+ * @tparam Op Unary operation as template class with a type member named `type`.
+ */
+template<typename... Type, template<typename...> class Op>
+struct type_list_transform<exclude_t<Type...>, Op> {
+    /*! @brief Resulting exclude list after applying the transform function. */
+    using type = exclude_t<typename Op<Type>::type...>;
+};
+
+/**
+ * @brief Applies a given _function_ to an owned list and generate a new list.
+ * @tparam Type Types provided by the owned list.
+ * @tparam Op Unary operation as template class with a type member named `type`.
+ */
+template<typename... Type, template<typename...> class Op>
+struct type_list_transform<owned_t<Type...>, Op> {
+    /*! @brief Resulting owned list after applying the transform function. */
+    using type = owned_t<typename Op<Type>::type...>;
+};
+
+/**
+ * @brief Provides a common way to define storage types.
+ * @tparam Type Storage value type.
+ * @tparam Entity A valid entity type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
+struct storage_type {
+    /*! @brief Type-to-storage conversion result. */
+    using type = sigh_mixin<basic_storage<Type, Entity, Allocator>>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Args Arguments to forward.
+ */
+template<typename... Args>
+using storage_type_t = typename storage_type<Args...>::type;
+
+/**
+ * Type-to-storage conversion utility that preserves constness.
+ * @tparam Type Storage value type, eventually const.
+ * @tparam Entity A valid entity type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
+struct storage_for {
+    /*! @brief Type-to-storage conversion result. */
+    using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Args Arguments to forward.
+ */
+template<typename... Args>
+using storage_for_t = typename storage_for<Args...>::type;
 
 /**
  * @brief Alias declaration for the most common use case.
- * @tparam Get Types of components iterated by the view.
- * @tparam Exclude Types of components used to filter the view.
+ * @tparam Get Types of storage iterated by the view.
+ * @tparam Exclude Types of storage used to filter the view.
  */
 template<typename Get, typename Exclude = exclude_t<>>
-using view = basic_view<entity, Get, Exclude>;
-
-/*! @brief Alias declaration for the most common use case. */
-using runtime_view = basic_runtime_view<sparse_set>;
+using view = basic_view<type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
 
 /**
  * @brief Alias declaration for the most common use case.
- * @tparam Args Other template parameters.
+ * @tparam Owned Types of storage _owned_ by the group.
+ * @tparam Get Types of storage _observed_ by the group.
+ * @tparam Exclude Types of storage used to filter the group.
  */
-template<typename... Args>
-using group = basic_group<entity, Args...>;
+template<typename Owned, typename Get, typename Exclude>
+using group = basic_group<type_list_transform_t<Owned, storage_for>, type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
 
 } // namespace entt
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 486 - 330
Dependencies/include/entt/entity/group.hpp


+ 128 - 83
Dependencies/include/entt/entity/handle.hpp

@@ -1,28 +1,110 @@
 #ifndef ENTT_ENTITY_HANDLE_HPP
 #define ENTT_ENTITY_HANDLE_HPP
 
+#include <iterator>
 #include <tuple>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
+#include "../core/iterator.hpp"
 #include "../core/type_traits.hpp"
+#include "entity.hpp"
 #include "fwd.hpp"
-#include "registry.hpp"
 
 namespace entt {
 
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename It>
+class handle_storage_iterator final {
+    template<typename Other>
+    friend class handle_storage_iterator;
+
+    using underlying_type = std::remove_reference_t<typename It::value_type::second_type>;
+    using entity_type = typename underlying_type::entity_type;
+
+public:
+    using value_type = typename std::iterator_traits<It>::value_type;
+    using pointer = input_iterator_pointer<value_type>;
+    using reference = value_type;
+    using difference_type = std::ptrdiff_t;
+    using iterator_category = std::input_iterator_tag;
+    using iterator_concept = std::forward_iterator_tag;
+
+    constexpr handle_storage_iterator() noexcept
+        : entt{null},
+          it{},
+          last{} {}
+
+    constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept
+        : entt{value},
+          it{from},
+          last{to} {
+        while(it != last && !it->second.contains(entt)) {
+            ++it;
+        }
+    }
+
+    constexpr handle_storage_iterator &operator++() noexcept {
+        while(++it != last && !it->second.contains(entt)) {}
+        return *this;
+    }
+
+    constexpr handle_storage_iterator operator++(int) noexcept {
+        handle_storage_iterator orig = *this;
+        return ++(*this), orig;
+    }
+
+    [[nodiscard]] constexpr reference operator*() const noexcept {
+        return *it;
+    }
+
+    [[nodiscard]] constexpr pointer operator->() const noexcept {
+        return operator*();
+    }
+
+    template<typename ILhs, typename IRhs>
+    friend constexpr bool operator==(const handle_storage_iterator<ILhs> &, const handle_storage_iterator<IRhs> &) noexcept;
+
+private:
+    entity_type entt;
+    It it;
+    It last;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] constexpr bool operator==(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
+    return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
 /**
  * @brief Non-owning handle to an entity.
  *
  * Tiny wrapper around a registry and an entity.
  *
- * @tparam Entity A valid entity type (see entt_traits for more details).
- * @tparam Type Types to which to restrict the scope of a handle.
+ * @tparam Registry Basic registry type.
+ * @tparam Scope Types to which to restrict the scope of a handle.
  */
-template<typename Entity, typename... Type>
+template<typename Registry, typename... Scope>
 struct basic_handle {
     /*! @brief Type of registry accepted by the handle. */
-    using registry_type = constness_as_t<basic_registry<std::remove_const_t<Entity>>, Entity>;
+    using registry_type = Registry;
     /*! @brief Underlying entity identifier. */
     using entity_type = typename registry_type::entity_type;
     /*! @brief Underlying version type. */
@@ -31,7 +113,7 @@ struct basic_handle {
     using size_type = typename registry_type::size_type;
 
     /*! @brief Constructs an invalid handle. */
-    basic_handle() ENTT_NOEXCEPT
+    basic_handle() noexcept
         : reg{},
           entt{null} {}
 
@@ -40,21 +122,37 @@ struct basic_handle {
      * @param ref An instance of the registry class.
      * @param value A valid identifier.
      */
-    basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
+    basic_handle(registry_type &ref, entity_type value) noexcept
         : reg{&ref},
           entt{value} {}
 
+    /**
+     * @brief Returns an iterable object to use to _visit_ a handle.
+     *
+     * The iterable object returns a pair that contains the name and a reference
+     * to the current storage.<br/>
+     * Returned storage are those that contain the entity associated with the
+     * handle.
+     *
+     * @return An iterable object to use to _visit_ the handle.
+     */
+    [[nodiscard]] auto storage() const noexcept {
+        auto iterable = reg->storage();
+        using iterator_type = internal::handle_storage_iterator<typename decltype(iterable)::iterator>;
+        return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}};
+    }
+
     /**
      * @brief Constructs a const handle from a non-const one.
-     * @tparam Other A valid entity type (see entt_traits for more details).
+     * @tparam Other A valid entity type.
      * @tparam Args Scope of the handle to construct.
      * @return A const handle referring to the same registry and the same
      * entity.
      */
     template<typename Other, typename... Args>
-    operator basic_handle<Other, Args...>() const ENTT_NOEXCEPT {
-        static_assert(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>, "Invalid conversion between different handles");
-        static_assert((sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))), "Invalid conversion between different handles");
+    operator basic_handle<Other, Args...>() const noexcept {
+        static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
+        static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
 
         return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
     }
@@ -63,7 +161,7 @@ struct basic_handle {
      * @brief Converts a handle to its underlying entity.
      * @return The contained identifier.
      */
-    [[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
+    [[nodiscard]] operator entity_type() const noexcept {
         return entity();
     }
 
@@ -71,7 +169,7 @@ struct basic_handle {
      * @brief Checks if a handle refers to non-null registry pointer and entity.
      * @return True if the handle refers to non-null registry and entity, false otherwise.
      */
-    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+    [[nodiscard]] explicit operator bool() const noexcept {
         return reg && reg->valid(entt);
     }
 
@@ -87,7 +185,7 @@ struct basic_handle {
      * @brief Returns a pointer to the underlying registry, if any.
      * @return A pointer to the underlying registry, if any.
      */
-    [[nodiscard]] registry_type *registry() const ENTT_NOEXCEPT {
+    [[nodiscard]] registry_type *registry() const noexcept {
         return reg;
     }
 
@@ -95,30 +193,25 @@ struct basic_handle {
      * @brief Returns the entity associated with a handle.
      * @return The entity associated with the handle.
      */
-    [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
+    [[nodiscard]] entity_type entity() const noexcept {
         return entt;
     }
 
-    /**
-     * @brief Destroys the entity associated with a handle.
-     * @sa basic_registry::destroy
-     */
+    /*! @brief Destroys the entity associated with a handle. */
     void destroy() {
-        reg->destroy(entt);
+        reg->destroy(std::exchange(entt, null));
     }
 
     /**
      * @brief Destroys the entity associated with a handle.
-     * @sa basic_registry::destroy
      * @param version A desired version upon destruction.
      */
     void destroy(const version_type version) {
-        reg->destroy(entt, version);
+        reg->destroy(std::exchange(entt, null), version);
     }
 
     /**
      * @brief Assigns the given component to a handle.
-     * @sa basic_registry::emplace
      * @tparam Component Type of component to create.
      * @tparam Args Types of arguments to use to construct the component.
      * @param args Parameters to use to initialize the component.
@@ -126,13 +219,12 @@ struct basic_handle {
      */
     template<typename Component, typename... Args>
     decltype(auto) emplace(Args &&...args) const {
-        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+        static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
         return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
     }
 
     /**
      * @brief Assigns or replaces the given component for a handle.
-     * @sa basic_registry::emplace_or_replace
      * @tparam Component Type of component to assign or replace.
      * @tparam Args Types of arguments to use to construct the component.
      * @param args Parameters to use to initialize the component.
@@ -140,13 +232,12 @@ struct basic_handle {
      */
     template<typename Component, typename... Args>
     decltype(auto) emplace_or_replace(Args &&...args) const {
-        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+        static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
         return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
     }
 
     /**
      * @brief Patches the given component for a handle.
-     * @sa basic_registry::patch
      * @tparam Component Type of component to patch.
      * @tparam Func Types of the function objects to invoke.
      * @param func Valid function objects.
@@ -154,13 +245,12 @@ struct basic_handle {
      */
     template<typename Component, typename... Func>
     decltype(auto) patch(Func &&...func) const {
-        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+        static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
         return reg->template patch<Component>(entt, std::forward<Func>(func)...);
     }
 
     /**
      * @brief Replaces the given component for a handle.
-     * @sa basic_registry::replace
      * @tparam Component Type of component to replace.
      * @tparam Args Types of arguments to use to construct the component.
      * @param args Parameters to use to initialize the component.
@@ -168,36 +258,33 @@ struct basic_handle {
      */
     template<typename Component, typename... Args>
     decltype(auto) replace(Args &&...args) const {
-        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+        static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
         return reg->template replace<Component>(entt, std::forward<Args>(args)...);
     }
 
     /**
      * @brief Removes the given components from a handle.
-     * @sa basic_registry::remove
      * @tparam Component Types of components to remove.
      * @return The number of components actually removed.
      */
     template<typename... Component>
     size_type remove() const {
-        static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
+        static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
         return reg->template remove<Component...>(entt);
     }
 
     /**
      * @brief Erases the given components from a handle.
-     * @sa basic_registry::erase
      * @tparam Component Types of components to erase.
      */
     template<typename... Component>
     void erase() const {
-        static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
+        static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
         reg->template erase<Component...>(entt);
     }
 
     /**
      * @brief Checks if a handle has all the given components.
-     * @sa basic_registry::all_of
      * @tparam Component Components for which to perform the check.
      * @return True if the handle has all the components, false otherwise.
      */
@@ -208,7 +295,6 @@ struct basic_handle {
 
     /**
      * @brief Checks if a handle has at least one of the given components.
-     * @sa basic_registry::any_of
      * @tparam Component Components for which to perform the check.
      * @return True if the handle has at least one of the given components,
      * false otherwise.
@@ -220,19 +306,17 @@ struct basic_handle {
 
     /**
      * @brief Returns references to the given components for a handle.
-     * @sa basic_registry::get
      * @tparam Component Types of components to get.
      * @return References to the components owned by the handle.
      */
     template<typename... Component>
     [[nodiscard]] decltype(auto) get() const {
-        static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
+        static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
         return reg->template get<Component...>(entt);
     }
 
     /**
      * @brief Returns a reference to the given component for a handle.
-     * @sa basic_registry::get_or_emplace
      * @tparam Component Type of component to get.
      * @tparam Args Types of arguments to use to construct the component.
      * @param args Parameters to use to initialize the component.
@@ -240,19 +324,18 @@ struct basic_handle {
      */
     template<typename Component, typename... Args>
     [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
-        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+        static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
         return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
     }
 
     /**
      * @brief Returns pointers to the given components for a handle.
-     * @sa basic_registry::try_get
      * @tparam Component Types of components to get.
      * @return Pointers to the components owned by the handle.
      */
     template<typename... Component>
     [[nodiscard]] auto try_get() const {
-        static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
+        static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
         return reg->template try_get<Component...>(entt);
     }
 
@@ -264,30 +347,6 @@ struct basic_handle {
         return reg->orphan(entt);
     }
 
-    /**
-     * @brief Visits a handle and returns the pools for its components.
-     *
-     * The signature of the function should be equivalent to the following:
-     *
-     * @code{.cpp}
-     * void(id_type, const basic_sparse_set<entity_type> &);
-     * @endcode
-     *
-     * Returned pools are those that contain the entity associated with the
-     * handle.
-     *
-     * @tparam Func Type of the function object to invoke.
-     * @param func A valid function object.
-     */
-    template<typename Func>
-    void visit(Func &&func) const {
-        for(auto [id, storage]: reg->storage()) {
-            if(storage.contains(entt)) {
-                func(id, storage);
-            }
-        }
-    }
-
 private:
     registry_type *reg;
     entity_type entt;
@@ -303,7 +362,7 @@ private:
  * entity, false otherwise.
  */
 template<typename... Args, typename... Other>
-[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
     return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity();
 }
 
@@ -317,24 +376,10 @@ template<typename... Args, typename... Other>
  * entity, true otherwise.
  */
 template<typename... Args, typename... Other>
-[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
     return !(lhs == rhs);
 }
 
-/**
- * @brief Deduction guide.
- * @tparam Entity A valid entity type (see entt_traits for more details).
- */
-template<typename Entity>
-basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>;
-
-/**
- * @brief Deduction guide.
- * @tparam Entity A valid entity type (see entt_traits for more details).
- */
-template<typename Entity>
-basic_handle(const basic_registry<Entity> &, Entity) -> basic_handle<const Entity>;
-
 } // namespace entt
 
 #endif

+ 190 - 73
Dependencies/include/entt/entity/helper.hpp

@@ -1,124 +1,113 @@
 #ifndef ENTT_ENTITY_HELPER_HPP
 #define ENTT_ENTITY_HELPER_HPP
 
+#include <memory>
 #include <type_traits>
-#include "../config/config.h"
+#include <utility>
 #include "../core/fwd.hpp"
 #include "../core/type_traits.hpp"
 #include "../signal/delegate.hpp"
 #include "fwd.hpp"
-#include "registry.hpp"
+#include "group.hpp"
+#include "storage.hpp"
+#include "view.hpp"
 
 namespace entt {
 
 /**
  * @brief Converts a registry to a view.
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Registry Basic registry type.
  */
-template<typename Entity>
-struct as_view {
-    /*! @brief Underlying entity identifier. */
-    using entity_type = std::remove_const_t<Entity>;
+template<typename Registry>
+class as_view {
+    template<typename... Get, typename... Exclude>
+    auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
+        return reg.template view<constness_as_t<typename Get::value_type, Get>...>(exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
+    }
+
+public:
     /*! @brief Type of registry to convert. */
-    using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
+    using registry_type = Registry;
+    /*! @brief Underlying entity identifier. */
+    using entity_type = typename registry_type::entity_type;
 
     /**
      * @brief Constructs a converter for a given registry.
      * @param source A valid reference to a registry.
      */
-    as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
+    as_view(registry_type &source) noexcept
+        : reg{source} {}
 
     /**
      * @brief Conversion function from a registry to a view.
-     * @tparam Exclude Types of components used to filter the view.
-     * @tparam Component Type of components used to construct the view.
+     * @tparam Get Type of storage used to construct the view.
+     * @tparam Exclude Types of storage used to filter the view.
      * @return A newly created view.
      */
-    template<typename Exclude, typename... Component>
-    operator basic_view<entity_type, get_t<Component...>, Exclude>() const {
-        return reg.template view<Component...>(Exclude{});
+    template<typename Get, typename Exclude>
+    operator basic_view<Get, Exclude>() const {
+        return dispatch(Get{}, Exclude{});
     }
 
 private:
     registry_type &reg;
 };
 
-/**
- * @brief Deduction guide.
- * @tparam Entity A valid entity type (see entt_traits for more details).
- */
-template<typename Entity>
-as_view(basic_registry<Entity> &) -> as_view<Entity>;
-
-/**
- * @brief Deduction guide.
- * @tparam Entity A valid entity type (see entt_traits for more details).
- */
-template<typename Entity>
-as_view(const basic_registry<Entity> &) -> as_view<const Entity>;
-
 /**
  * @brief Converts a registry to a group.
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Registry Basic registry type.
  */
-template<typename Entity>
-struct as_group {
-    /*! @brief Underlying entity identifier. */
-    using entity_type = std::remove_const_t<Entity>;
+template<typename Registry>
+class as_group {
+    template<typename... Owned, typename... Get, typename... Exclude>
+    auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
+        if constexpr(std::is_const_v<registry_type>) {
+            return reg.template group_if_exists<typename Owned::value_type...>(get_t<typename Get::value_type...>{}, exclude_t<typename Exclude::value_type...>{});
+        } else {
+            return reg.template group<constness_as_t<typename Owned::value_type, Owned>...>(get_t<constness_as_t<typename Get::value_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
+        }
+    }
+
+public:
     /*! @brief Type of registry to convert. */
-    using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
+    using registry_type = Registry;
+    /*! @brief Underlying entity identifier. */
+    using entity_type = typename registry_type::entity_type;
 
     /**
      * @brief Constructs a converter for a given registry.
      * @param source A valid reference to a registry.
      */
-    as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
+    as_group(registry_type &source) noexcept
+        : reg{source} {}
 
     /**
      * @brief Conversion function from a registry to a group.
-     * @tparam Get Types of components observed by the group.
-     * @tparam Exclude Types of components used to filter the group.
-     * @tparam Owned Types of components owned by the group.
+     * @tparam Owned Types of _owned_ by the group.
+     * @tparam Get Types of storage _observed_ by the group.
+     * @tparam Exclude Types of storage used to filter the group.
      * @return A newly created group.
      */
-    template<typename Get, typename Exclude, typename... Owned>
-    operator basic_group<entity_type, owned_t<Owned...>, Get, Exclude>() const {
-        if constexpr(std::is_const_v<registry_type>) {
-            return reg.template group_if_exists<Owned...>(Get{}, Exclude{});
-        } else {
-            return reg.template group<Owned...>(Get{}, Exclude{});
-        }
+    template<typename Owned, typename Get, typename Exclude>
+    operator basic_group<Owned, Get, Exclude>() const {
+        return dispatch(Owned{}, Get{}, Exclude{});
     }
 
 private:
     registry_type &reg;
 };
 
-/**
- * @brief Deduction guide.
- * @tparam Entity A valid entity type (see entt_traits for more details).
- */
-template<typename Entity>
-as_group(basic_registry<Entity> &) -> as_group<Entity>;
-
-/**
- * @brief Deduction guide.
- * @tparam Entity A valid entity type (see entt_traits for more details).
- */
-template<typename Entity>
-as_group(const basic_registry<Entity> &) -> as_group<const Entity>;
-
 /**
  * @brief Helper to create a listener that directly invokes a member function.
  * @tparam Member Member function to invoke on a component of the given type.
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Registry Basic registry type.
  * @param reg A registry that contains the given entity and its components.
  * @param entt Entity from which to get the component.
  */
-template<auto Member, typename Entity = entity>
-void invoke(basic_registry<Entity> &reg, const Entity entt) {
+template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, decltype(Member)>>>
+void invoke(Registry &reg, const typename Registry::entity_type entt) {
     static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
-    delegate<void(basic_registry<Entity> &, const Entity)> func;
+    delegate<void(Registry &, const typename Registry::entity_type)> func;
     func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
     func(reg, entt);
 }
@@ -127,23 +116,22 @@ void invoke(basic_registry<Entity> &reg, const Entity entt) {
  * @brief Returns the entity associated with a given component.
  *
  * @warning
- * Currently, this function only works correctly with the default pool as it
+ * Currently, this function only works correctly with the default storage as it
  * makes assumptions about how the components are laid out.
  *
- * @tparam Entity A valid entity type (see entt_traits for more details).
- * @tparam Component Type of component.
- * @param reg A registry that contains the given entity and its components.
+ * @tparam Args Storage type template parameters.
+ * @param storage A storage that contains the given component.
  * @param instance A valid component instance.
  * @return The entity associated with the given component.
  */
-template<typename Entity, typename Component>
-Entity to_entity(const basic_registry<Entity> &reg, const Component &instance) {
-    const auto &storage = reg.template storage<Component>();
-    const typename basic_registry<Entity>::base_type &base = storage;
+template<typename... Args>
+auto to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) -> typename basic_storage<Args...>::entity_type {
+    constexpr auto page_size = basic_storage<Args...>::traits_type::page_size;
+    const typename basic_storage<Args...>::base_type &base = storage;
     const auto *addr = std::addressof(instance);
 
-    for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) {
-        if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
+    for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
+        if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
             return *(it + dist);
         }
     }
@@ -151,6 +139,135 @@ Entity to_entity(const basic_registry<Entity> &reg, const Component &instance) {
     return null;
 }
 
+/**
+ * @copybrief to_entity
+ * @tparam Args Registry type template parameters.
+ * @tparam Component Type of component.
+ * @param reg A registry that contains the given entity and its components.
+ * @param instance A valid component instance.
+ * @return The entity associated with the given component.
+ */
+template<typename... Args, typename Component>
+[[deprecated("use storage based to_entity instead")]] typename basic_registry<Args...>::entity_type to_entity(const basic_registry<Args...> &reg, const Component &instance) {
+    if(const auto *storage = reg.template storage<Component>(); storage) {
+        return to_entity(*storage, instance);
+    }
+
+    return null;
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct sigh_helper;
+
+/**
+ * @brief Signal connection helper for registries.
+ * @tparam Registry Basic registry type.
+ */
+template<typename Registry>
+struct sigh_helper<Registry> {
+    /*! @brief Registry type. */
+    using registry_type = Registry;
+
+    /**
+     * @brief Constructs a helper for a given registry.
+     * @param ref A valid reference to a registry.
+     */
+    sigh_helper(registry_type &ref)
+        : bucket{&ref} {}
+
+    /**
+     * @brief Binds a properly initialized helper to a given signal type.
+     * @tparam Type Type of signal to bind the helper to.
+     * @param id Optional name for the underlying storage to use.
+     * @return A helper for a given registry and signal type.
+     */
+    template<typename Type>
+    auto with(const id_type id = type_hash<Type>::value()) noexcept {
+        return sigh_helper<registry_type, Type>{*bucket, id};
+    }
+
+    /**
+     * @brief Returns a reference to the underlying registry.
+     * @return A reference to the underlying registry.
+     */
+    [[nodiscard]] registry_type &registry() noexcept {
+        return *bucket;
+    }
+
+private:
+    registry_type *bucket;
+};
+
+/**
+ * @brief Signal connection helper for registries.
+ * @tparam Registry Basic registry type.
+ * @tparam Type Type of signal to connect listeners to.
+ */
+template<typename Registry, typename Type>
+struct sigh_helper<Registry, Type> final: sigh_helper<Registry> {
+    /*! @brief Registry type. */
+    using registry_type = Registry;
+
+    /**
+     * @brief Constructs a helper for a given registry.
+     * @param ref A valid reference to a registry.
+     * @param id Optional name for the underlying storage to use.
+     */
+    sigh_helper(registry_type &ref, const id_type id = type_hash<Type>::value())
+        : sigh_helper<Registry>{ref},
+          name{id} {}
+
+    /**
+     * @brief Forwards the call to `on_construct` on the underlying storage.
+     * @tparam Candidate Function or member to connect.
+     * @tparam Args Type of class or type of payload, if any.
+     * @param args A valid object that fits the purpose, if any.
+     * @return This helper.
+     */
+    template<auto Candidate, typename... Args>
+    auto on_construct(Args &&...args) {
+        this->registry().template on_construct<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
+        return *this;
+    }
+
+    /**
+     * @brief Forwards the call to `on_update` on the underlying storage.
+     * @tparam Candidate Function or member to connect.
+     * @tparam Args Type of class or type of payload, if any.
+     * @param args A valid object that fits the purpose, if any.
+     * @return This helper.
+     */
+    template<auto Candidate, typename... Args>
+    auto on_update(Args &&...args) {
+        this->registry().template on_update<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
+        return *this;
+    }
+
+    /**
+     * @brief Forwards the call to `on_destroy` on the underlying storage.
+     * @tparam Candidate Function or member to connect.
+     * @tparam Args Type of class or type of payload, if any.
+     * @param args A valid object that fits the purpose, if any.
+     * @return This helper.
+     */
+    template<auto Candidate, typename... Args>
+    auto on_destroy(Args &&...args) {
+        this->registry().template on_destroy<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
+        return *this;
+    }
+
+private:
+    id_type name;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Registry Basic registry type.
+ */
+template<typename Registry>
+sigh_helper(Registry &) -> sigh_helper<Registry>;
+
 } // namespace entt
 
 #endif

+ 302 - 0
Dependencies/include/entt/entity/mixin.hpp

@@ -0,0 +1,302 @@
+#ifndef ENTT_ENTITY_MIXIN_HPP
+#define ENTT_ENTITY_MIXIN_HPP
+
+#include <type_traits>
+#include <utility>
+#include "../config/config.h"
+#include "../core/any.hpp"
+#include "../signal/sigh.hpp"
+#include "entity.hpp"
+#include "fwd.hpp"
+
+namespace entt {
+
+/**
+ * @brief Mixin type used to add signal support to storage types.
+ *
+ * The function type of a listener is equivalent to:
+ *
+ * @code{.cpp}
+ * void(basic_registry<entity_type> &, entity_type);
+ * @endcode
+ *
+ * This applies to all signals made available.
+ *
+ * @tparam Type Underlying storage type.
+ * @tparam Registry Basic registry type.
+ */
+template<typename Type, typename Registry>
+class basic_sigh_mixin final: public Type {
+    using underlying_type = Type;
+    using owner_type = Registry;
+
+    using basic_registry_type = basic_registry<typename underlying_type::entity_type, typename underlying_type::base_type::allocator_type>;
+    using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
+    using underlying_iterator = typename underlying_type::base_type::basic_iterator;
+
+    static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
+
+    owner_type &owner_or_assert() const noexcept {
+        ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+        return static_cast<owner_type &>(*owner);
+    }
+
+    void pop(underlying_iterator first, underlying_iterator last) final {
+        if(auto &reg = owner_or_assert(); destruction.empty()) {
+            underlying_type::pop(first, last);
+        } else {
+            for(; first != last; ++first) {
+                const auto entt = *first;
+                destruction.publish(reg, entt);
+                const auto it = underlying_type::find(entt);
+                underlying_type::pop(it, it + 1u);
+            }
+        }
+    }
+
+    void pop_all() final {
+        if(auto &reg = owner_or_assert(); !destruction.empty()) {
+            for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) {
+                if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
+                    destruction.publish(reg, *it);
+                } else {
+                    if constexpr(underlying_type::traits_type::in_place_delete) {
+                        if(const auto entt = *it; entt != tombstone) {
+                            destruction.publish(reg, entt);
+                        }
+                    } else {
+                        destruction.publish(reg, *it);
+                    }
+                }
+            }
+        }
+
+        underlying_type::pop_all();
+    }
+
+    underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final {
+        const auto it = underlying_type::try_emplace(entt, force_back, value);
+
+        if(auto &reg = owner_or_assert(); it != underlying_type::base_type::end()) {
+            construction.publish(reg, *it);
+        }
+
+        return it;
+    }
+
+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_sigh_mixin()
+        : basic_sigh_mixin{allocator_type{}} {}
+
+    /**
+     * @brief Constructs an empty storage with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit basic_sigh_mixin(const allocator_type &allocator)
+        : underlying_type{allocator},
+          owner{},
+          construction{allocator},
+          destruction{allocator},
+          update{allocator} {}
+
+    /**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+    basic_sigh_mixin(basic_sigh_mixin &&other) noexcept
+        : underlying_type{std::move(other)},
+          owner{other.owner},
+          construction{std::move(other.construction)},
+          destruction{std::move(other.destruction)},
+          update{std::move(other.update)} {}
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept
+        : underlying_type{std::move(other), allocator},
+          owner{other.owner},
+          construction{std::move(other.construction), allocator},
+          destruction{std::move(other.destruction), allocator},
+          update{std::move(other.update), allocator} {}
+
+    /**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This storage.
+     */
+    basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept {
+        underlying_type::operator=(std::move(other));
+        owner = other.owner;
+        construction = std::move(other.construction);
+        destruction = std::move(other.destruction);
+        update = std::move(other.update);
+        return *this;
+    }
+
+    /**
+     * @brief Exchanges the contents with those of a given storage.
+     * @param other Storage to exchange the content with.
+     */
+    void swap(basic_sigh_mixin &other) {
+        using std::swap;
+        underlying_type::swap(other);
+        swap(owner, other.owner);
+        swap(construction, other.construction);
+        swap(destruction, other.destruction);
+        swap(update, other.update);
+    }
+
+    /**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever a new instance is created and assigned to an entity.<br/>
+     * Listeners are invoked after the object has been assigned to the entity.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+    [[nodiscard]] auto on_construct() noexcept {
+        return sink{construction};
+    }
+
+    /**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever an instance is explicitly updated.<br/>
+     * Listeners are invoked after the object has been updated.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+    [[nodiscard]] auto on_update() noexcept {
+        return sink{update};
+    }
+
+    /**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever an instance is removed from an entity and thus destroyed.<br/>
+     * Listeners are invoked before the object has been removed from the entity.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+    [[nodiscard]] auto on_destroy() noexcept {
+        return sink{destruction};
+    }
+
+    /**
+     * @brief Emplace elements into a storage.
+     *
+     * The behavior of this operation depends on the underlying storage type
+     * (for example, components vs entities).<br/>
+     * Refer to the specific documentation for more details.
+     *
+     * @return A return value as returned by the underlying storage.
+     */
+    auto emplace() {
+        const auto entt = underlying_type::emplace();
+        construction.publish(owner_or_assert(), entt);
+        return entt;
+    }
+
+    /**
+     * @brief Emplace elements into a storage.
+     *
+     * The behavior of this operation depends on the underlying storage type
+     * (for example, components vs entities).<br/>
+     * Refer to the specific documentation for more details.
+     *
+     * @tparam Args Types of arguments to forward to the underlying storage.
+     * @param hint A valid identifier.
+     * @param args Parameters to forward to the underlying storage.
+     * @return A return value as returned by the underlying storage.
+     */
+    template<typename... Args>
+    decltype(auto) emplace(const entity_type hint, Args &&...args) {
+        if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
+            const auto entt = underlying_type::emplace(hint, std::forward<Args>(args)...);
+            construction.publish(owner_or_assert(), entt);
+            return entt;
+        } else {
+            underlying_type::emplace(hint, std::forward<Args>(args)...);
+            construction.publish(owner_or_assert(), hint);
+            return this->get(hint);
+        }
+    }
+
+    /**
+     * @brief Patches the given instance for an entity.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the patched instance.
+     */
+    template<typename... Func>
+    decltype(auto) patch(const entity_type entt, Func &&...func) {
+        underlying_type::patch(entt, std::forward<Func>(func)...);
+        update.publish(owner_or_assert(), entt);
+        return this->get(entt);
+    }
+
+    /**
+     * @brief Emplace elements into a storage.
+     *
+     * The behavior of this operation depends on the underlying storage type
+     * (for example, components vs entities).<br/>
+     * Refer to the specific documentation for more details.
+     *
+     * @tparam It Iterator type (as required by the underlying storage type).
+     * @tparam Args Types of arguments to forward to the underlying storage.
+     * @param first An iterator to the first element of the range.
+     * @param last An iterator past the last element of the range.
+     * @param args Parameters to use to forward to the underlying storage.
+     */
+    template<typename It, typename... Args>
+    void insert(It first, It last, Args &&...args) {
+        underlying_type::insert(first, last, std::forward<Args>(args)...);
+
+        if(auto &reg = owner_or_assert(); !construction.empty()) {
+            for(; first != last; ++first) {
+                construction.publish(reg, *first);
+            }
+        }
+    }
+
+    /**
+     * @brief Forwards variables to derived classes, if any.
+     * @param value A variable wrapped in an opaque container.
+     */
+    void bind(any value) noexcept final {
+        auto *reg = any_cast<basic_registry_type>(&value);
+        owner = reg ? reg : owner;
+        underlying_type::bind(std::move(value));
+    }
+
+private:
+    basic_registry_type *owner;
+    sigh_type construction;
+    sigh_type destruction;
+    sigh_type update;
+};
+
+} // namespace entt
+
+#endif

+ 77 - 77
Dependencies/include/entt/entity/observer.hpp

@@ -6,14 +6,10 @@
 #include <limits>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
 #include "../core/type_traits.hpp"
 #include "../signal/delegate.hpp"
-#include "entity.hpp"
 #include "fwd.hpp"
-#include "registry.hpp"
 #include "storage.hpp"
-#include "utility.hpp"
 
 namespace entt {
 
@@ -47,7 +43,7 @@ struct basic_collector<> {
      * @return The updated collector.
      */
     template<typename... AllOf, typename... NoneOf>
-    static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
+    static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
         return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
     }
 
@@ -57,7 +53,7 @@ struct basic_collector<> {
      * @return The updated collector.
      */
     template<typename AnyOf>
-    static constexpr auto update() ENTT_NOEXCEPT {
+    static constexpr auto update() noexcept {
         return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
     }
 };
@@ -82,7 +78,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
      * @return The updated collector.
      */
     template<typename... AllOf, typename... NoneOf>
-    static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
+    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...>{};
     }
 
@@ -92,7 +88,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
      * @return The updated collector.
      */
     template<typename AnyOf>
-    static constexpr auto update() ENTT_NOEXCEPT {
+    static constexpr auto update() noexcept {
         return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
     }
 
@@ -103,7 +99,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
      * @return The updated collector.
      */
     template<typename... AllOf, typename... NoneOf>
-    static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
+    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...>{};
     }
@@ -150,8 +146,7 @@ inline constexpr basic_collector<> collector{};
  * * The entity currently pointed is destroyed.
  *
  * In all the other cases, modifying the pools of the given components in any
- * way invalidates all the iterators and using them results in undefined
- * behavior.
+ * way invalidates all the iterators.
  *
  * @warning
  * Lifetime of an observer doesn't necessarily have to overcome that of the
@@ -159,11 +154,13 @@ inline constexpr basic_collector<> collector{};
  * from the registry before being destroyed to avoid crashes due to dangling
  * pointers.
  *
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Registry Basic registry type.
+ * @tparam Mask Mask type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Entity>
-class basic_observer {
-    using payload_type = std::uint32_t;
+template<typename Registry, typename Mask, typename Allocator>
+class basic_observer: private basic_storage<Mask, typename Registry::entity_type, Allocator> {
+    using base_type = basic_storage<Mask, typename Registry::entity_type, Allocator>;
 
     template<typename>
     struct matcher_handler;
@@ -171,43 +168,43 @@ class basic_observer {
     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(basic_observer &obs, basic_registry<Entity> &reg, const Entity entt) {
+        static void maybe_valid_if(basic_observer &obs, Registry &reg, const typename Registry::entity_type entt) {
             if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
-                if(!obs.storage.contains(entt)) {
-                    obs.storage.emplace(entt);
+                if(!obs.contains(entt)) {
+                    obs.emplace(entt);
                 }
 
-                obs.storage.get(entt) |= (1 << Index);
+                obs.get(entt) |= (1 << Index);
             }
         }
 
         template<std::size_t Index>
-        static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
-            if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
-                obs.storage.erase(entt);
+        static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
+            if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
+                obs.erase(entt);
             }
         }
 
         template<std::size_t Index>
-        static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
+        static void connect(basic_observer &obs, Registry &reg) {
             (reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
             (reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
             reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
             reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
         }
 
-        static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
-            (reg.template on_destroy<Require>().disconnect(obs), ...);
-            (reg.template on_construct<Reject>().disconnect(obs), ...);
-            reg.template on_update<AnyOf>().disconnect(obs);
-            reg.template on_destroy<AnyOf>().disconnect(obs);
+        static void disconnect(basic_observer &obs, Registry &reg) {
+            (reg.template on_destroy<Require>().disconnect(&obs), ...);
+            (reg.template on_construct<Reject>().disconnect(&obs), ...);
+            reg.template on_update<AnyOf>().disconnect(&obs);
+            reg.template on_destroy<AnyOf>().disconnect(&obs);
         }
     };
 
     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(basic_observer &obs, basic_registry<Entity> &reg, const Entity entt) {
+        static void maybe_valid_if(basic_observer &obs, Registry &reg, const typename Registry::entity_type entt) {
             auto condition = [&reg, entt]() {
                 if constexpr(sizeof...(Ignore) == 0) {
                     return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
@@ -217,23 +214,23 @@ class basic_observer {
             };
 
             if(condition()) {
-                if(!obs.storage.contains(entt)) {
-                    obs.storage.emplace(entt);
+                if(!obs.contains(entt)) {
+                    obs.emplace(entt);
                 }
 
-                obs.storage.get(entt) |= (1 << Index);
+                obs.get(entt) |= (1 << Index);
             }
         }
 
         template<std::size_t Index>
-        static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
-            if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
-                obs.storage.erase(entt);
+        static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
+            if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
+                obs.erase(entt);
             }
         }
 
         template<std::size_t Index>
-        static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
+        static void connect(basic_observer &obs, Registry &reg) {
             (reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
             (reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
             (reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
@@ -242,43 +239,55 @@ class basic_observer {
             (reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
         }
 
-        static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
-            (reg.template on_destroy<Require>().disconnect(obs), ...);
-            (reg.template on_construct<Reject>().disconnect(obs), ...);
-            (reg.template on_construct<AllOf>().disconnect(obs), ...);
-            (reg.template on_destroy<NoneOf>().disconnect(obs), ...);
-            (reg.template on_destroy<AllOf>().disconnect(obs), ...);
-            (reg.template on_construct<NoneOf>().disconnect(obs), ...);
+        static void disconnect(basic_observer &obs, Registry &reg) {
+            (reg.template on_destroy<Require>().disconnect(&obs), ...);
+            (reg.template on_construct<Reject>().disconnect(&obs), ...);
+            (reg.template on_construct<AllOf>().disconnect(&obs), ...);
+            (reg.template on_destroy<NoneOf>().disconnect(&obs), ...);
+            (reg.template on_destroy<AllOf>().disconnect(&obs), ...);
+            (reg.template on_construct<NoneOf>().disconnect(&obs), ...);
         }
     };
 
     template<typename... Matcher>
-    static void disconnect(basic_registry<Entity> &reg, basic_observer &obs) {
+    static void disconnect(Registry &reg, basic_observer &obs) {
         (matcher_handler<Matcher>::disconnect(obs, reg), ...);
     }
 
     template<typename... Matcher, std::size_t... Index>
-    void connect(basic_registry<Entity> &reg, std::index_sequence<Index...>) {
-        static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers");
+    void connect(Registry &reg, std::index_sequence<Index...>) {
+        static_assert(sizeof...(Matcher) < std::numeric_limits<typename base_type::value_type>::digits, "Too many matchers");
         (matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
         release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
     }
 
 public:
+    /*! Basic registry type. */
+    using registry_type = Registry;
     /*! @brief Underlying entity identifier. */
-    using entity_type = Entity;
+    using entity_type = typename registry_type::entity_type;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
     /*! @brief Random access iterator type. */
-    using iterator = typename basic_sparse_set<Entity>::iterator;
+    using iterator = typename registry_type::common_type::iterator;
 
     /*! @brief Default constructor. */
     basic_observer()
-        : release{},
-          storage{} {}
+        : 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)
+        : base_type{allocator},
+          release{} {}
 
     /*! @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;
 
@@ -286,16 +295,14 @@ public:
      * @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(basic_registry<entity_type> &reg, basic_collector<Matcher...>)
-        : basic_observer{} {
+    basic_observer(registry_type &reg, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
+        : basic_observer{allocator} {
         connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
     }
 
-    /*! @brief Default destructor. */
-    ~basic_observer() = default;
-
     /**
      * @brief Default copy assignment operator, deleted on purpose.
      * @return This observer.
@@ -314,10 +321,10 @@ public:
      * @param reg A valid reference to a registry.
      */
     template<typename... Matcher>
-    void connect(basic_registry<entity_type> &reg, basic_collector<Matcher...>) {
+    void connect(registry_type &reg, basic_collector<Matcher...>) {
         disconnect();
         connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
-        storage.clear();
+        base_type::clear();
     }
 
     /*! @brief Disconnects an observer from the registry it keeps track of. */
@@ -332,16 +339,16 @@ public:
      * @brief Returns the number of elements in an observer.
      * @return Number of elements.
      */
-    [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
-        return storage.size();
+    [[nodiscard]] size_type size() const noexcept {
+        return base_type::size();
     }
 
     /**
      * @brief Checks whether an observer is empty.
      * @return True if the observer is empty, false otherwise.
      */
-    [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
-        return storage.empty();
+    [[nodiscard]] bool empty() const noexcept {
+        return base_type::empty();
     }
 
     /**
@@ -356,39 +363,33 @@ public:
      *
      * @return A pointer to the array of entities.
      */
-    [[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
-        return storage.data();
+    [[nodiscard]] const entity_type *data() const noexcept {
+        return base_type::data();
     }
 
     /**
      * @brief Returns an iterator to the first entity of the observer.
      *
-     * The returned iterator points to the first entity of the observer. If the
-     * container is empty, the returned iterator will be equal to `end()`.
+     * 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 ENTT_NOEXCEPT {
-        return storage.basic_sparse_set<entity_type>::begin();
+    [[nodiscard]] iterator begin() const noexcept {
+        return base_type::base_type::begin();
     }
 
     /**
      * @brief Returns an iterator that is past the last entity of the observer.
-     *
-     * The returned iterator points to the entity following the last entity of
-     * the observer. Attempting to dereference the returned iterator results in
-     * undefined behavior.
-     *
      * @return An iterator to the entity following the last entity of the
      * observer.
      */
-    [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-        return storage.basic_sparse_set<entity_type>::end();
+    [[nodiscard]] iterator end() const noexcept {
+        return base_type::base_type::end();
     }
 
     /*! @brief Clears the underlying container. */
-    void clear() ENTT_NOEXCEPT {
-        storage.clear();
+    void clear() noexcept {
+        base_type::clear();
     }
 
     /**
@@ -428,7 +429,6 @@ public:
 
 private:
     delegate<void(basic_observer &)> release;
-    basic_storage<entity_type, payload_type> storage;
 };
 
 } // namespace entt

+ 74 - 149
Dependencies/include/entt/entity/organizer.hpp

@@ -1,15 +1,15 @@
 #ifndef ENTT_ENTITY_ORGANIZER_HPP
 #define ENTT_ENTITY_ORGANIZER_HPP
 
-#include <algorithm>
 #include <cstddef>
 #include <type_traits>
 #include <utility>
 #include <vector>
-#include "../container/dense_hash_map.hpp"
 #include "../core/type_info.hpp"
 #include "../core/type_traits.hpp"
 #include "../core/utility.hpp"
+#include "../graph/adjacency_matrix.hpp"
+#include "../graph/flow.hpp"
 #include "fwd.hpp"
 #include "helper.hpp"
 
@@ -25,8 +25,8 @@ namespace internal {
 template<typename>
 struct is_view: std::false_type {};
 
-template<typename Entity, typename... Component, typename... Exclude>
-struct is_view<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>>: std::true_type {};
+template<typename... Args>
+struct is_view<basic_view<Args...>>: std::true_type {};
 
 template<typename Type>
 inline constexpr bool is_view_v = is_view<Type>::value;
@@ -34,60 +34,57 @@ inline constexpr bool is_view_v = is_view<Type>::value;
 template<typename Type, typename Override>
 struct unpack_type {
     using ro = std::conditional_t<
-        type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
+        type_list_contains_v<Override, const Type> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
         type_list<std::remove_const_t<Type>>,
         type_list<>>;
 
     using rw = std::conditional_t<
-        type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>),
+        type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, const Type>),
         type_list<Type>,
         type_list<>>;
 };
 
-template<typename Entity, typename... Override>
-struct unpack_type<basic_registry<Entity>, type_list<Override...>> {
+template<typename... Args, typename... Override>
+struct unpack_type<basic_registry<Args...>, type_list<Override...>> {
     using ro = type_list<>;
     using rw = type_list<>;
 };
 
-template<typename Entity, typename... Override>
-struct unpack_type<const basic_registry<Entity>, type_list<Override...>>
-    : unpack_type<basic_registry<Entity>, type_list<Override...>> {};
+template<typename... Args, typename... Override>
+struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
+    : unpack_type<basic_registry<Args...>, type_list<Override...>> {};
 
-template<typename Entity, typename... Component, typename... Exclude, typename... Override>
-struct unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {
-    using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>;
-    using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>;
+template<typename... Get, typename... Exclude, typename... Override>
+struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
+    using ro = type_list_cat_t<type_list<typename Exclude::value_type...>, typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::ro...>;
+    using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::rw...>;
 };
 
-template<typename Entity, typename... Component, typename... Exclude, typename... Override>
-struct unpack_type<const basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>>
-    : unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {};
+template<typename... Get, typename... Exclude, typename... Override>
+struct unpack_type<const basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
+    : unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
 
 template<typename, typename>
-struct resource;
+struct resource_traits;
 
 template<typename... Args, typename... Req>
-struct resource<type_list<Args...>, type_list<Req...>> {
+struct resource_traits<type_list<Args...>, type_list<Req...>> {
     using args = type_list<std::remove_const_t<Args>...>;
     using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
     using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
 };
 
 template<typename... Req, typename Ret, typename... Args>
-resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource(Ret (*)(Args...));
+resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
 
 template<typename... Req, typename Ret, typename Type, typename... Args>
-resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret (*)(Type &, Args...));
+resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
 
 template<typename... Req, typename Ret, typename Class, typename... Args>
-resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret (Class::*)(Args...));
+resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
 
 template<typename... Req, typename Ret, typename Class, typename... Args>
-resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret (Class::*)(Args...) const);
-
-template<typename... Req>
-resource<type_list<>, type_list<Req...>> to_resource();
+resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
 
 } // namespace internal
 
@@ -105,12 +102,12 @@ resource<type_list<>, type_list<Req...>> to_resource();
  * goal of the tool. Instead, they are returned to the user in the form of a
  * graph that allows for safe execution.
  *
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Registry Basic registry type.
  */
-template<typename Entity>
+template<typename Registry>
 class basic_organizer final {
-    using callback_type = void(const void *, basic_registry<Entity> &);
-    using prepare_type = void(basic_registry<Entity> &);
+    using callback_type = void(const void *, Registry &);
+    using prepare_type = void(Registry &);
     using dependency_type = std::size_t(const bool, const type_info **, const std::size_t);
 
     struct vertex_data final {
@@ -125,18 +122,18 @@ class basic_organizer final {
     };
 
     template<typename Type>
-    [[nodiscard]] static decltype(auto) extract(basic_registry<Entity> &reg) {
-        if constexpr(std::is_same_v<Type, basic_registry<Entity>>) {
+    [[nodiscard]] static decltype(auto) extract(Registry &reg) {
+        if constexpr(std::is_same_v<Type, Registry>) {
             return reg;
         } else if constexpr(internal::is_view_v<Type>) {
-            return as_view{reg};
+            return static_cast<Type>(as_view{reg});
         } else {
-            return reg.template ctx_or_set<std::remove_reference_t<Type>>();
+            return reg.ctx().template emplace<std::remove_reference_t<Type>>();
         }
     }
 
     template<typename... Args>
-    [[nodiscard]] static auto to_args(basic_registry<Entity> &reg, type_list<Args...>) {
+    [[nodiscard]] static auto to_args(Registry &reg, type_list<Args...>) {
         return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
     }
 
@@ -146,92 +143,29 @@ class basic_organizer final {
             return {};
         } else {
             const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
-            const auto length = (std::min)(count, sizeof...(Type));
-            std::copy_n(info, length, buffer);
+            const auto length = count < sizeof...(Type) ? count : sizeof...(Type);
+
+            for(std::size_t pos{}; pos < length; ++pos) {
+                buffer[pos] = info[pos];
+            }
+
             return length;
         }
     }
 
     template<typename... RO, typename... RW>
     void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
-        dependencies[type_hash<basic_registry<Entity>>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
-        (dependencies[type_hash<RO>::value()].emplace_back(index, false), ...);
-        (dependencies[type_hash<RW>::value()].emplace_back(index, true), ...);
-    }
-
-    [[nodiscard]] std::vector<bool> adjacency_matrix() {
-        const auto length = vertices.size();
-        std::vector<bool> edges(length * length, false);
-
-        // creates the ajacency matrix
-        for(const auto &deps: dependencies) {
-            const auto last = deps.second.cend();
-            auto it = deps.second.cbegin();
-
-            while(it != last) {
-                if(it->second) {
-                    // rw item
-                    if(auto curr = it++; it != last) {
-                        if(it->second) {
-                            edges[curr->first * length + it->first] = true;
-                        } else {
-                            if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
-                                for(; it != next; ++it) {
-                                    edges[curr->first * length + it->first] = true;
-                                    edges[it->first * length + next->first] = true;
-                                }
-                            } else {
-                                for(; it != next; ++it) {
-                                    edges[curr->first * length + it->first] = true;
-                                }
-                            }
-                        }
-                    }
-                } else {
-                    // ro item, possibly only on first iteration
-                    if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
-                        for(; it != next; ++it) {
-                            edges[it->first * length + next->first] = true;
-                        }
-                    } else {
-                        it = last;
-                    }
-                }
-            }
-        }
-
-        // computes the transitive closure
-        for(std::size_t vk{}; vk < length; ++vk) {
-            for(std::size_t vi{}; vi < length; ++vi) {
-                for(std::size_t vj{}; vj < length; ++vj) {
-                    edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]);
-                }
-            }
-        }
-
-        // applies the transitive reduction
-        for(std::size_t vert{}; vert < length; ++vert) {
-            edges[vert * length + vert] = false;
-        }
-
-        for(std::size_t vj{}; vj < length; ++vj) {
-            for(std::size_t vi{}; vi < length; ++vi) {
-                if(edges[vi * length + vj]) {
-                    for(std::size_t vk{}; vk < length; ++vk) {
-                        if(edges[vj * length + vk]) {
-                            edges[vi * length + vk] = false;
-                        }
-                    }
-                }
-            }
-        }
-
-        return edges;
+        builder.bind(static_cast<id_type>(index));
+        builder.set(type_hash<Registry>::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
+        (builder.ro(type_hash<RO>::value()), ...);
+        (builder.rw(type_hash<RW>::value()), ...);
     }
 
 public:
+    /*! Basic registry type. */
+    using registry_type = Registry;
     /*! @brief Underlying entity identifier. */
-    using entity_type = Entity;
+    using entity_type = typename registry_type::entity_type;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
     /*! @brief Raw task function type. */
@@ -257,7 +191,7 @@ public:
          * @param length The length of the user-supplied buffer.
          * @return The number of type info objects written to the buffer.
          */
-        size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
+        size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
             return node.dependency(false, buffer, length);
         }
 
@@ -268,7 +202,7 @@ public:
          * @param length The length of the user-supplied buffer.
          * @return The number of type info objects written to the buffer.
          */
-        size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
+        size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
             return node.dependency(true, buffer, length);
         }
 
@@ -276,7 +210,7 @@ public:
          * @brief Returns the number of read-only resources of a vertex.
          * @return The number of read-only resources of the vertex.
          */
-        size_type ro_count() const ENTT_NOEXCEPT {
+        size_type ro_count() const noexcept {
             return node.ro_count;
         }
 
@@ -284,7 +218,7 @@ public:
          * @brief Returns the number of writable resources of a vertex.
          * @return The number of writable resources of the vertex.
          */
-        size_type rw_count() const ENTT_NOEXCEPT {
+        size_type rw_count() const noexcept {
             return node.rw_count;
         }
 
@@ -292,7 +226,7 @@ public:
          * @brief Checks if a vertex is also a top-level one.
          * @return True if the vertex is a top-level one, false otherwise.
          */
-        bool top_level() const ENTT_NOEXCEPT {
+        bool top_level() const noexcept {
             return is_top_level;
         }
 
@@ -300,7 +234,7 @@ public:
          * @brief Returns a type info object associated with a vertex.
          * @return A properly initialized type info object.
          */
-        const type_info &info() const ENTT_NOEXCEPT {
+        const type_info &info() const noexcept {
             return *node.info;
         }
 
@@ -308,7 +242,7 @@ public:
          * @brief Returns a user defined name associated with a vertex, if any.
          * @return The user defined name associated with the vertex, if any.
          */
-        const char *name() const ENTT_NOEXCEPT {
+        const char *name() const noexcept {
             return node.name;
         }
 
@@ -316,7 +250,7 @@ public:
          * @brief Returns the function associated with a vertex.
          * @return The function associated with the vertex.
          */
-        function_type *callback() const ENTT_NOEXCEPT {
+        function_type *callback() const noexcept {
             return node.callback;
         }
 
@@ -324,7 +258,7 @@ public:
          * @brief Returns the payload associated with a vertex, if any.
          * @return The payload associated with the vertex, if any.
          */
-        const void *data() const ENTT_NOEXCEPT {
+        const void *data() const noexcept {
             return node.payload;
         }
 
@@ -332,7 +266,7 @@ public:
          * @brief Returns the list of nodes reachable from a given vertex.
          * @return The list of nodes reachable from the vertex.
          */
-        const std::vector<std::size_t> &children() const ENTT_NOEXCEPT {
+        const std::vector<std::size_t> &children() const noexcept {
             return reachable;
         }
 
@@ -341,7 +275,7 @@ public:
          * are properly instantiated before using them.
          * @param reg A valid registry.
          */
-        void prepare(basic_registry<entity_type> &reg) const {
+        void prepare(registry_type &reg) const {
             node.prepare ? node.prepare(reg) : void();
         }
 
@@ -359,10 +293,10 @@ public:
      */
     template<auto Candidate, typename... Req>
     void emplace(const char *name = nullptr) {
-        using resource_type = decltype(internal::free_function_to_resource<Req...>(Candidate));
-        constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
+        using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
+        constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
 
-        callback_type *callback = +[](const void *, basic_registry<entity_type> &reg) {
+        callback_type *callback = +[](const void *, registry_type &reg) {
             std::apply(Candidate, to_args(reg, typename resource_type::args{}));
         };
 
@@ -373,7 +307,7 @@ public:
             nullptr,
             callback,
             +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
-            +[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
+            +[](registry_type &reg) { void(to_args(reg, typename resource_type::args{})); },
             &type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
 
         track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
@@ -391,10 +325,10 @@ public:
      */
     template<auto Candidate, typename... Req, typename Type>
     void emplace(Type &value_or_instance, const char *name = nullptr) {
-        using resource_type = decltype(internal::constrained_function_to_resource<Req...>(Candidate));
-        constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
+        using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
+        constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
 
-        callback_type *callback = +[](const void *payload, basic_registry<entity_type> &reg) {
+        callback_type *callback = +[](const void *payload, registry_type &reg) {
             Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
             std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
         };
@@ -406,7 +340,7 @@ public:
             &value_or_instance,
             callback,
             +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
-            +[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
+            +[](registry_type &reg) { void(to_args(reg, typename resource_type::args{})); },
             &type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
 
         track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
@@ -423,7 +357,7 @@ public:
      */
     template<typename... Req>
     void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
-        using resource_type = internal::resource<type_list<>, type_list<Req...>>;
+        using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
         track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
 
         vertex_data vdata{
@@ -444,28 +378,19 @@ public:
      * @return The adjacency list of the task graph.
      */
     std::vector<vertex> graph() {
-        const auto edges = adjacency_matrix();
-
-        // creates the adjacency list
         std::vector<vertex> adjacency_list{};
         adjacency_list.reserve(vertices.size());
+        auto adjacency_matrix = builder.graph();
 
-        for(std::size_t col{}, length = vertices.size(); col < length; ++col) {
+        for(auto curr: adjacency_matrix.vertices()) {
+            const auto iterable = adjacency_matrix.in_edges(curr);
             std::vector<std::size_t> reachable{};
-            const auto row = col * length;
-            bool is_top_level = true;
-
-            for(std::size_t next{}; next < length; ++next) {
-                if(edges[row + next]) {
-                    reachable.push_back(next);
-                }
-            }
 
-            for(std::size_t next{}; next < length && is_top_level; ++next) {
-                is_top_level = !edges[next * length + col];
+            for(auto &&edge: adjacency_matrix.out_edges(curr)) {
+                reachable.push_back(edge.second);
             }
 
-            adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable));
+            adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable));
         }
 
         return adjacency_list;
@@ -473,13 +398,13 @@ public:
 
     /*! @brief Erases all elements from a container. */
     void clear() {
-        dependencies.clear();
+        builder.clear();
         vertices.clear();
     }
 
 private:
-    dense_hash_map<id_type, std::vector<std::pair<std::size_t, bool>>, identity> dependencies;
     std::vector<vertex_data> vertices;
+    flow builder;
 };
 
 } // namespace entt

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 312 - 421
Dependencies/include/entt/entity/registry.hpp


+ 116 - 72
Dependencies/include/entt/entity/runtime_view.hpp

@@ -2,14 +2,12 @@
 #define ENTT_ENTITY_RUNTIME_VIEW_HPP
 
 #include <algorithm>
+#include <cstddef>
 #include <iterator>
-#include <type_traits>
 #include <utility>
 #include <vector>
-#include "../config/config.h"
 #include "entity.hpp"
 #include "fwd.hpp"
-#include "sparse_set.hpp"
 
 namespace entt {
 
@@ -20,8 +18,10 @@ namespace entt {
 
 namespace internal {
 
-template<typename Type>
+template<typename Set>
 class runtime_view_iterator final {
+    using iterator_type = typename Set::iterator;
+
     [[nodiscard]] bool valid() const {
         return (!tombstone_check || *it != tombstone)
                && std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); })
@@ -29,16 +29,19 @@ class runtime_view_iterator final {
     }
 
 public:
-    using iterator_type = typename Type::iterator;
     using difference_type = typename iterator_type::difference_type;
     using value_type = typename iterator_type::value_type;
     using pointer = typename iterator_type::pointer;
     using reference = typename iterator_type::reference;
     using iterator_category = std::bidirectional_iterator_tag;
 
-    runtime_view_iterator() ENTT_NOEXCEPT = default;
+    constexpr runtime_view_iterator() noexcept
+        : pools{},
+          filter{},
+          it{},
+          tombstone_check{} {}
 
-    runtime_view_iterator(const std::vector<const Type *> &cpools, const std::vector<const Type *> &ignore, iterator_type curr) ENTT_NOEXCEPT
+    runtime_view_iterator(const std::vector<Set *> &cpools, const std::vector<Set *> &ignore, iterator_type curr) noexcept
         : pools{&cpools},
           filter{&ignore},
           it{curr},
@@ -58,35 +61,35 @@ public:
         return ++(*this), orig;
     }
 
-    runtime_view_iterator &operator--() ENTT_NOEXCEPT {
+    runtime_view_iterator &operator--() {
         while(--it != (*pools)[0]->begin() && !valid()) {}
         return *this;
     }
 
-    runtime_view_iterator operator--(int) ENTT_NOEXCEPT {
+    runtime_view_iterator operator--(int) {
         runtime_view_iterator orig = *this;
         return operator--(), orig;
     }
 
-    [[nodiscard]] pointer operator->() const {
+    [[nodiscard]] pointer operator->() const noexcept {
         return it.operator->();
     }
 
-    [[nodiscard]] reference operator*() const {
+    [[nodiscard]] reference operator*() const noexcept {
         return *operator->();
     }
 
-    [[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept {
         return it == other.it;
     }
 
-    [[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept {
         return !(*this == other);
     }
 
 private:
-    const std::vector<const Type *> *pools;
-    const std::vector<const Type *> *filter;
+    const std::vector<Set *> *pools;
+    const std::vector<Set *> *filter;
     iterator_type it;
     bool tombstone_check;
 };
@@ -98,76 +101,125 @@ private:
  * @endcond
  */
 
-/**
- * @brief Runtime view implementation.
- *
- * 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_runtime_view;
-
 /**
  * @brief Generic runtime view.
  *
- * Runtime views iterate over those entities that have at least all the given
- * components in their bags. During initialization, a runtime view looks at the
- * number of entities available for each component and picks up a reference to
- * the smallest set of candidate entities in order to get a performance boost
- * when iterate.<br/>
- * Order of elements during iterations are highly dependent on the order of the
- * underlying data structures. See sparse_set and its specializations for more
- * details.
+ * Runtime views iterate over those entities that are at least in the given
+ * storage. During initialization, a runtime view looks at the number of
+ * entities available for each component and uses the smallest set in order to
+ * get a performance boost when iterating.
  *
  * @b Important
  *
  * Iterators aren't invalidated if:
  *
- * * New instances of the given components are created and assigned to entities.
- * * The entity currently pointed is modified (as an example, if one of the
- *   given components is removed from the entity to which the iterator points).
+ * * New elements are added to the storage.
+ * * The entity currently pointed is modified (for example, components are added
+ *   or removed from it).
  * * The entity currently pointed is destroyed.
  *
- * In all the other cases, modifying the pools of the given components in any
- * way invalidates all the iterators and using them results in undefined
- * behavior.
- *
- * @note
- * Views share references to the underlying data structures of the registry that
- * generated them. Therefore any change to the entities and to the components
- * made by means of the registry are immediately reflected by the views, unless
- * a pool was missing when the view was built (in this case, the view won't
- * have a valid reference and won't be updated accordingly).
- *
- * @warning
- * Lifetime of a view must not overcome that of the registry that generated it.
- * In any other case, attempting to use a view results in undefined behavior.
+ * In all other cases, modifying the storage iterated by the view in any way
+ * invalidates all the iterators.
  *
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Type Common base type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Entity, typename Allocator>
-struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> final {
+template<typename Type, typename Allocator>
+class basic_runtime_view {
+    using alloc_traits = std::allocator_traits<Allocator>;
+    static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
+    using container_type = std::vector<Type *, Allocator>;
+
+public:
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
     /*! @brief Underlying entity identifier. */
-    using entity_type = Entity;
+    using entity_type = typename Type::entity_type;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
     /*! @brief Common type among all storage types. */
-    using base_type = basic_sparse_set<Entity, Allocator>;
+    using common_type = Type;
     /*! @brief Bidirectional iterator type. */
-    using iterator = internal::runtime_view_iterator<base_type>;
+    using iterator = internal::runtime_view_iterator<common_type>;
 
     /*! @brief Default constructor to use to create empty, invalid views. */
-    basic_runtime_view() ENTT_NOEXCEPT
-        : pools{},
-          filter{} {}
+    basic_runtime_view() noexcept
+        : basic_runtime_view{allocator_type{}} {}
+
+    /**
+     * @brief Constructs an empty, invalid view with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit basic_runtime_view(const allocator_type &allocator)
+        : pools{allocator},
+          filter{allocator} {}
+
+    /*! @brief Default copy constructor. */
+    basic_runtime_view(const basic_runtime_view &) = default;
+
+    /**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+    basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator)
+        : pools{other.pools, allocator},
+          filter{other.filter, allocator} {}
+
+    /*! @brief Default move constructor. */
+    basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator)
+        : pools{std::move(other.pools), allocator},
+          filter{std::move(other.filter), allocator} {}
+
+    /**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+    basic_runtime_view &operator=(const basic_runtime_view &) = default;
+
+    /**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+    basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
+
+    /**
+     * @brief Exchanges the contents with those of a given view.
+     * @param other View to exchange the content with.
+     */
+    void swap(basic_runtime_view &other) {
+        using std::swap;
+        swap(pools, other.pools);
+        swap(filter, other.filter);
+    }
+
+    /**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return pools.get_allocator();
+    }
+
+    /*! @brief Clears the view. */
+    void clear() {
+        pools.clear();
+        filter.clear();
+    }
 
     /**
      * @brief Appends an opaque storage object to a runtime view.
      * @param base An opaque reference to a storage object.
      * @return This runtime view.
      */
-    basic_runtime_view &iterate(const base_type &base) {
+    basic_runtime_view &iterate(common_type &base) {
         if(pools.empty() || !(base.size() < pools[0u]->size())) {
             pools.push_back(&base);
         } else {
@@ -182,7 +234,7 @@ struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> final {
      * @param base An opaque reference to a storage object.
      * @return This runtime view.
      */
-    basic_runtime_view &exclude(const base_type &base) {
+    basic_runtime_view &exclude(common_type &base) {
         filter.push_back(&base);
         return *this;
     }
@@ -199,9 +251,7 @@ struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> final {
      * @brief Returns an iterator to the first entity that has the given
      * components.
      *
-     * The returned iterator points to the first entity that has the given
-     * components. If the view is empty, the returned iterator will be equal to
-     * `end()`.
+     * If the view is empty, the returned iterator will be equal to `end()`.
      *
      * @return An iterator to the first entity that has the given components.
      */
@@ -212,11 +262,6 @@ struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> final {
     /**
      * @brief Returns an iterator that is past the last entity that has the
      * given components.
-     *
-     * The returned iterator points to the entity following the last entity that
-     * has the given components. Attempting to dereference the returned iterator
-     * results in undefined behavior.
-     *
      * @return An iterator to the entity following the last entity that has the
      * given components.
      */
@@ -239,8 +284,7 @@ struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> final {
      * @brief Iterates entities and applies the given function object to them.
      *
      * The function object is invoked for each entity. It is provided only with
-     * the entity itself. To get the components, users can use the registry with
-     * which the view was built.<br/>
+     * the entity itself.<br/>
      * The signature of the function should be equivalent to the following:
      *
      * @code{.cpp}
@@ -258,8 +302,8 @@ struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> final {
     }
 
 private:
-    std::vector<const base_type *> pools;
-    std::vector<const base_type *> filter;
+    container_type pools;
+    container_type filter;
 };
 
 } // namespace entt

+ 210 - 311
Dependencies/include/entt/entity/snapshot.hpp

@@ -1,7 +1,6 @@
 #ifndef ENTT_ENTITY_SNAPSHOT_HPP
 #define ENTT_ENTITY_SNAPSHOT_HPP
 
-#include <array>
 #include <cstddef>
 #include <iterator>
 #include <tuple>
@@ -9,142 +8,140 @@
 #include <utility>
 #include <vector>
 #include "../config/config.h"
-#include "../container/dense_hash_map.hpp"
+#include "../container/dense_map.hpp"
 #include "../core/type_traits.hpp"
-#include "component.hpp"
 #include "entity.hpp"
 #include "fwd.hpp"
-#include "registry.hpp"
+#include "view.hpp"
 
 namespace entt {
 
 /**
- * @brief Utility class to create snapshots from a registry.
- *
- * A _snapshot_ can be either a dump of the entire registry or a narrower
- * selection of components of interest.<br/>
- * This type can be used in both cases if provided with a correctly configured
- * output archive.
- *
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
  */
-template<typename Entity>
-class basic_snapshot {
-    using entity_traits = entt_traits<Entity>;
 
-    template<typename Component, typename Archive, typename It>
-    void get(Archive &archive, std::size_t sz, It first, It last) const {
-        const auto view = reg->template view<std::add_const_t<Component>>();
-        archive(typename entity_traits::entity_type(sz));
+namespace internal {
 
-        while(first != last) {
-            const auto entt = *(first++);
+template<typename Registry>
+void orphans(Registry &registry) {
+    auto &storage = registry.template storage<typename Registry::entity_type>();
 
-            if(reg->template all_of<Component>(entt)) {
-                std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt)));
-            }
+    for(auto entt: storage) {
+        if(registry.orphan(entt)) {
+            storage.erase(entt);
         }
     }
+}
 
-    template<typename... Component, typename Archive, typename It, std::size_t... Index>
-    void component(Archive &archive, It first, It last, std::index_sequence<Index...>) const {
-        std::array<std::size_t, sizeof...(Index)> size{};
-        auto begin = first;
+} // namespace internal
 
-        while(begin != last) {
-            const auto entt = *(begin++);
-            ((reg->template all_of<Component>(entt) ? ++size[Index] : 0u), ...);
-        }
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
 
-        (get<Component>(archive, size[Index], first, last), ...);
-    }
+/**
+ * @brief Utility class to create snapshots from a registry.
+ *
+ * A _snapshot_ can be either a dump of the entire registry or a narrower
+ * selection of components of interest.<br/>
+ * This type can be used in both cases if provided with a correctly configured
+ * output archive.
+ *
+ * @tparam Registry Basic registry type.
+ */
+template<typename Registry>
+class basic_snapshot {
+    static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
+    using traits_type = typename Registry::traits_type;
 
 public:
+    /*! Basic registry type. */
+    using registry_type = Registry;
     /*! @brief Underlying entity identifier. */
-    using entity_type = Entity;
+    using entity_type = typename registry_type::entity_type;
 
     /**
      * @brief Constructs an instance that is bound to a given registry.
      * @param source A valid reference to a registry.
      */
-    basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT
+    basic_snapshot(const registry_type &source) noexcept
         : reg{&source} {}
 
     /*! @brief Default move constructor. */
-    basic_snapshot(basic_snapshot &&) = default;
+    basic_snapshot(basic_snapshot &&) noexcept = default;
 
     /*! @brief Default move assignment operator. @return This snapshot. */
-    basic_snapshot &operator=(basic_snapshot &&) = default;
+    basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
 
     /**
-     * @brief Puts aside all the entities from the underlying registry.
-     *
-     * Entities are serialized along with their versions. Destroyed entities are
-     * taken in consideration as well by this function.
-     *
+     * @brief Serializes all elements of a type with associated identifiers.
+     * @tparam Type Type of elements to serialize.
      * @tparam Archive Type of output archive.
      * @param archive A valid reference to an output archive.
+     * @param id Optional name used to map the storage within the registry.
      * @return An object of this type to continue creating the snapshot.
      */
-    template<typename Archive>
-    const basic_snapshot &entities(Archive &archive) const {
-        const auto sz = reg->size();
+    template<typename Type, typename Archive>
+    const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const {
+        if(const auto *storage = reg->template storage<Type>(id); storage) {
+            archive(static_cast<typename traits_type::entity_type>(storage->size()));
 
-        archive(typename entity_traits::entity_type(sz + 1u));
-        archive(reg->released());
+            if constexpr(std::is_same_v<Type, entity_type>) {
+                archive(static_cast<typename traits_type::entity_type>(storage->free_list()));
 
-        for(auto first = reg->data(), last = first + sz; first != last; ++first) {
-            archive(*first);
+                for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
+                    archive(*first);
+                }
+            } else {
+                for(auto elem: storage->reach()) {
+                    std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem);
+                }
+            }
+        } else {
+            archive(typename traits_type::entity_type{});
         }
 
         return *this;
     }
 
     /**
-     * @brief Puts aside the given components.
-     *
-     * Each instance is serialized together with the entity to which it belongs.
-     * Entities are serialized along with their versions.
-     *
-     * @tparam Component Types of components to serialize.
-     * @tparam Archive Type of output archive.
-     * @param archive A valid reference to an output archive.
-     * @return An object of this type to continue creating the snapshot.
-     */
-    template<typename... Component, typename Archive>
-    const basic_snapshot &component(Archive &archive) const {
-        if constexpr(sizeof...(Component) == 1u) {
-            const auto view = reg->template view<const Component...>();
-            (component<Component>(archive, view.rbegin(), view.rend()), ...);
-            return *this;
-        } else {
-            (component<Component>(archive), ...);
-            return *this;
-        }
-    }
-
-    /**
-     * @brief Puts aside the given components for the entities in a range.
-     *
-     * Each instance is serialized together with the entity to which it belongs.
-     * Entities are serialized along with their versions.
-     *
-     * @tparam Component Types of components to serialize.
+     * @brief Serializes all elements of a type with associated identifiers for
+     * the entities in a range.
+     * @tparam Type Type of elements to serialize.
      * @tparam Archive Type of output archive.
      * @tparam It Type of input iterator.
      * @param archive A valid reference to an output archive.
      * @param first An iterator to the first element of the range to serialize.
      * @param last An iterator past the last element of the range to serialize.
+     * @param id Optional name used to map the storage within the registry.
      * @return An object of this type to continue creating the snapshot.
      */
-    template<typename... Component, typename Archive, typename It>
-    const basic_snapshot &component(Archive &archive, It first, It last) const {
-        component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
+    template<typename Type, typename Archive, typename It>
+    const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash<Type>::value()) const {
+        static_assert(!std::is_same_v<Type, entity_type>, "Entity types not supported");
+
+        if(const auto *storage = reg->template storage<Type>(id); storage && !storage->empty()) {
+            archive(static_cast<typename traits_type::entity_type>(std::distance(first, last)));
+
+            for(; first != last; ++first) {
+                if(const auto entt = *first; storage->contains(entt)) {
+                    archive(entt);
+                    std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
+                } else {
+                    archive(static_cast<entity_type>(null));
+                }
+            }
+        } else {
+            archive(typename traits_type::entity_type{});
+        }
+
         return *this;
     }
 
 private:
-    const basic_registry<entity_type> *reg;
+    const registry_type *reg;
 };
 
 /**
@@ -155,100 +152,82 @@ private:
  * originally had.<br/>
  * An example of use is the implementation of a save/restore utility.
  *
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Registry Basic registry type.
  */
-template<typename Entity>
+template<typename Registry>
 class basic_snapshot_loader {
-    using entity_traits = entt_traits<Entity>;
-
-    template<typename Type, typename Archive>
-    void assign(Archive &archive) const {
-        typename entity_traits::entity_type length{};
-        archive(length);
-
-        entity_type entt{};
-
-        if constexpr(ignore_as_empty_v<std::remove_const_t<Type>>) {
-            while(length--) {
-                archive(entt);
-                const auto entity = reg->valid(entt) ? entt : reg->create(entt);
-                ENTT_ASSERT(entity == entt, "Entity not available for use");
-                reg->template emplace<Type>(entity);
-            }
-        } else {
-            Type instance{};
-
-            while(length--) {
-                archive(entt, instance);
-                const auto entity = reg->valid(entt) ? entt : reg->create(entt);
-                ENTT_ASSERT(entity == entt, "Entity not available for use");
-                reg->template emplace<Type>(entity, std::move(instance));
-            }
-        }
-    }
+    static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
+    using traits_type = typename Registry::traits_type;
 
 public:
+    /*! Basic registry type. */
+    using registry_type = Registry;
     /*! @brief Underlying entity identifier. */
-    using entity_type = Entity;
+    using entity_type = typename registry_type::entity_type;
 
     /**
      * @brief Constructs an instance that is bound to a given registry.
      * @param source A valid reference to a registry.
      */
-    basic_snapshot_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
+    basic_snapshot_loader(registry_type &source) noexcept
         : reg{&source} {
         // restoring a snapshot as a whole requires a clean registry
-        ENTT_ASSERT(reg->empty(), "Registry must be empty");
+        ENTT_ASSERT(reg->template storage<entity_type>().empty() && (reg->storage().begin() == reg->storage().end()), "Registry must be empty");
     }
 
     /*! @brief Default move constructor. */
-    basic_snapshot_loader(basic_snapshot_loader &&) = default;
+    basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default;
 
     /*! @brief Default move assignment operator. @return This loader. */
-    basic_snapshot_loader &operator=(basic_snapshot_loader &&) = default;
+    basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
 
     /**
-     * @brief Restores entities that were in use during serialization.
-     *
-     * This function restores the entities that were in use during serialization
-     * and gives them the versions they originally had.
-     *
+     * @brief Restores all elements of a type with associated identifiers.
+     * @tparam Type Type of elements to restore.
      * @tparam Archive Type of input archive.
      * @param archive A valid reference to an input archive.
+     * @param id Optional name used to map the storage within the registry.
      * @return A valid loader to continue restoring data.
      */
-    template<typename Archive>
-    const basic_snapshot_loader &entities(Archive &archive) const {
-        typename entity_traits::entity_type length{};
+    template<typename Type, typename Archive>
+    basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
+        auto &storage = reg->template storage<Type>(id);
+        typename traits_type::entity_type length{};
 
         archive(length);
-        std::vector<entity_type> all(length);
 
-        for(std::size_t pos{}; pos < length; ++pos) {
-            archive(all[pos]);
-        }
+        if constexpr(std::is_same_v<Type, entity_type>) {
+            typename traits_type::entity_type count{};
 
-        reg->assign(++all.cbegin(), all.cend(), all[0u]);
+            storage.reserve(length);
+            archive(count);
 
-        return *this;
-    }
+            for(entity_type entity = null; length; --length) {
+                archive(entity);
+                storage.emplace(entity);
+            }
+
+            storage.free_list(count);
+        } else {
+            auto &other = reg->template storage<entity_type>();
+            entity_type entt{null};
+
+            while(length--) {
+                if(archive(entt); entt != null) {
+                    const auto entity = other.contains(entt) ? entt : other.emplace(entt);
+                    ENTT_ASSERT(entity == entt, "Entity not available for use");
+
+                    if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
+                        storage.emplace(entity);
+                    } else {
+                        Type elem{};
+                        archive(elem);
+                        storage.emplace(entity, std::move(elem));
+                    }
+                }
+            }
+        }
 
-    /**
-     * @brief Restores components and assigns them to the right entities.
-     *
-     * The template parameter list must be exactly the same used during
-     * serialization. In the event that the entity to which the component is
-     * assigned doesn't exist yet, the loader will take care to create it with
-     * the version it originally had.
-     *
-     * @tparam Component Types of components to restore.
-     * @tparam Archive Type of input archive.
-     * @param archive A valid reference to an input archive.
-     * @return A valid loader to continue restoring data.
-     */
-    template<typename... Component, typename Archive>
-    const basic_snapshot_loader &component(Archive &archive) const {
-        (assign<Component>(archive), ...);
         return *this;
     }
 
@@ -258,22 +237,17 @@ public:
      * In case all the entities were serialized but only part of the components
      * was saved, it could happen that some of the entities have no components
      * once restored.<br/>
-     * This functions helps to identify and destroy those entities.
+     * This function helps to identify and destroy those entities.
      *
      * @return A valid loader to continue restoring data.
      */
-    const basic_snapshot_loader &orphans() const {
-        reg->each([this](const auto entt) {
-            if(reg->orphan(entt)) {
-                reg->release(entt);
-            }
-        });
-
+    basic_snapshot_loader &orphans() {
+        internal::orphans(*reg);
         return *this;
     }
 
 private:
-    basic_registry<entity_type> *reg;
+    registry_type *reg;
 };
 
 /**
@@ -286,43 +260,29 @@ private:
  * Identifiers that entities originally had are not transferred to the target.
  * Instead, the loader maps remote identifiers to local ones while restoring a
  * snapshot.<br/>
- * An example of use is the implementation of a client-server applications with
+ * An example of use is the implementation of a client-server application with
  * the requirement of transferring somehow parts of the representation side to
  * side.
  *
- * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Registry Basic registry type.
  */
-template<typename Entity>
+template<typename Registry>
 class basic_continuous_loader {
-    using entity_traits = entt_traits<Entity>;
-
-    void destroy(Entity entt) {
-        if(const auto it = remloc.find(entt); it == remloc.cend()) {
-            const auto local = reg->create();
-            remloc.emplace(entt, std::make_pair(local, true));
-            reg->destroy(local);
-        }
-    }
-
-    void restore(Entity entt) {
-        const auto it = remloc.find(entt);
+    static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
+    using traits_type = typename Registry::traits_type;
 
-        if(it == remloc.cend()) {
-            const auto local = reg->create();
-            remloc.emplace(entt, std::make_pair(local, true));
-        } else {
-            if(!reg->valid(remloc[entt].first)) {
-                remloc[entt].first = reg->create();
+    void restore(typename Registry::entity_type entt) {
+        if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
+            if(!reg->valid(remloc[entity].second)) {
+                remloc[entity].second = reg->create();
             }
-
-            // set the dirty flag
-            remloc[entt].second = true;
+        } else {
+            remloc.insert_or_assign(entity, std::make_pair(entt, reg->create()));
         }
     }
 
     template<typename Container>
-    auto update(int, Container &container)
-        -> decltype(typename Container::mapped_type{}, void()) {
+    auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) {
         // map like container
         Container other;
 
@@ -340,12 +300,12 @@ class basic_continuous_loader {
             }
         }
 
-        std::swap(container, other);
+        using std::swap;
+        swap(container, other);
     }
 
     template<typename Container>
-    auto update(char, Container &container)
-        -> decltype(typename Container::value_type{}, void()) {
+    auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) {
         // vector like container
         static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type");
 
@@ -354,9 +314,9 @@ class basic_continuous_loader {
         }
     }
 
-    template<typename Other, typename Type, typename Member>
-    void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type::*member) {
-        if constexpr(!std::is_same_v<Other, Type>) {
+    template<typename Component, typename Other, typename Member>
+    void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) {
+        if constexpr(!std::is_same_v<Component, Other>) {
             return;
         } else if constexpr(std::is_same_v<Member, entity_type>) {
             instance.*member = map(instance.*member);
@@ -366,52 +326,19 @@ class basic_continuous_loader {
         }
     }
 
-    template<typename Component>
-    void remove_if_exists() {
-        for(auto &&ref: remloc) {
-            const auto local = ref.second.first;
-
-            if(reg->valid(local)) {
-                reg->template remove<Component>(local);
-            }
-        }
-    }
-
-    template<typename Other, typename Archive, typename... Type, typename... Member>
-    void assign(Archive &archive, [[maybe_unused]] Member Type::*...member) {
-        typename entity_traits::entity_type length{};
-        archive(length);
-
-        entity_type entt{};
-
-        if constexpr(ignore_as_empty_v<std::remove_const_t<Other>>) {
-            while(length--) {
-                archive(entt);
-                restore(entt);
-                reg->template emplace_or_replace<Other>(map(entt));
-            }
-        } else {
-            Other instance{};
-
-            while(length--) {
-                archive(entt, instance);
-                (update(instance, member), ...);
-                restore(entt);
-                reg->template emplace_or_replace<Other>(map(entt), std::move(instance));
-            }
-        }
-    }
-
 public:
+    /*! Basic registry type. */
+    using registry_type = Registry;
     /*! @brief Underlying entity identifier. */
-    using entity_type = Entity;
+    using entity_type = typename registry_type::entity_type;
 
     /**
      * @brief Constructs an instance that is bound to a given registry.
      * @param source A valid reference to a registry.
      */
-    basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
-        : reg{&source} {}
+    basic_continuous_loader(registry_type &source) noexcept
+        : remloc{source.get_allocator()},
+          reg{&source} {}
 
     /*! @brief Default move constructor. */
     basic_continuous_loader(basic_continuous_loader &&) = default;
@@ -420,87 +347,66 @@ public:
     basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
 
     /**
-     * @brief Restores entities that were in use during serialization.
+     * @brief Restores all elements of a type with associated identifiers.
      *
-     * This function restores the entities that were in use during serialization
-     * and creates local counterparts for them if required.
+     * It creates local counterparts for remote elements as needed.<br/>
+     * Members are either data members of type entity_type or containers of
+     * entities. In both cases, a loader visits them and replaces entities with
+     * their local counterpart.
      *
+     * @tparam Type Type of elements to restore.
      * @tparam Archive Type of input archive.
      * @param archive A valid reference to an input archive.
-     * @return A non-const reference to this loader.
+     * @param id Optional name used to map the storage within the registry.
+     * @return A valid loader to continue restoring data.
      */
-    template<typename Archive>
-    basic_continuous_loader &entities(Archive &archive) {
-        typename entity_traits::entity_type length{};
-        entity_type entt{};
+    template<typename Type, typename Archive>
+    basic_continuous_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
+        auto &storage = reg->template storage<Type>(id);
+        typename traits_type::entity_type length{};
+        entity_type entt{null};
 
         archive(length);
-        // discards the head of the list of destroyed entities
-        archive(entt);
 
-        for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) {
-            archive(entt);
+        if constexpr(std::is_same_v<Type, entity_type>) {
+            typename traits_type::entity_type in_use{};
 
-            if(const auto entity = entity_traits::to_entity(entt); entity == pos) {
+            storage.reserve(length);
+            archive(in_use);
+
+            for(std::size_t pos{}; pos < in_use; ++pos) {
+                archive(entt);
                 restore(entt);
-            } else {
-                destroy(entt);
             }
-        }
 
-        return *this;
-    }
-
-    /**
-     * @brief Restores components and assigns them to the right entities.
-     *
-     * The template parameter list must be exactly the same used during
-     * serialization. In the event that the entity to which the component is
-     * assigned doesn't exist yet, the loader will take care to create a local
-     * counterpart for it.<br/>
-     * Members can be either data members of type entity_type or containers of
-     * entities. In both cases, the loader will visit them and update the
-     * entities by replacing each one with its local counterpart.
-     *
-     * @tparam Component Type of component to restore.
-     * @tparam Archive Type of input archive.
-     * @tparam Type Types of components to update with local counterparts.
-     * @tparam Member Types of members to update with their local counterparts.
-     * @param archive A valid reference to an input archive.
-     * @param member Members to update with their local counterparts.
-     * @return A non-const reference to this loader.
-     */
-    template<typename... Component, typename Archive, typename... Type, typename... Member>
-    basic_continuous_loader &component(Archive &archive, Member Type::*...member) {
-        (remove_if_exists<Component>(), ...);
-        (assign<Component>(archive, member...), ...);
-        return *this;
-    }
-
-    /**
-     * @brief Helps to purge entities that no longer have a conterpart.
-     *
-     * Users should invoke this member function after restoring each snapshot,
-     * unless they know exactly what they are doing.
-     *
-     * @return A non-const reference to this loader.
-     */
-    basic_continuous_loader &shrink() {
-        auto it = remloc.begin();
+            for(std::size_t pos = in_use; pos < length; ++pos) {
+                archive(entt);
 
-        while(it != remloc.cend()) {
-            const auto local = it->second.first;
-            bool &dirty = it->second.second;
+                if(const auto entity = to_entity(entt); remloc.contains(entity)) {
+                    if(reg->valid(remloc[entity].second)) {
+                        reg->destroy(remloc[entity].second);
+                    }
 
-            if(dirty) {
-                dirty = false;
-                ++it;
-            } else {
-                if(reg->valid(local)) {
-                    reg->destroy(local);
+                    remloc.erase(entity);
                 }
+            }
+        } else {
+            for(auto &&ref: remloc) {
+                storage.remove(ref.second.second);
+            }
 
-                it = remloc.erase(it);
+            while(length--) {
+                if(archive(entt); entt != null) {
+                    restore(entt);
+
+                    if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
+                        storage.emplace(map(entt));
+                    } else {
+                        Type elem{};
+                        archive(elem);
+                        storage.emplace(map(entt), std::move(elem));
+                    }
+                }
             }
         }
 
@@ -513,17 +419,12 @@ public:
      * In case all the entities were serialized but only part of the components
      * was saved, it could happen that some of the entities have no components
      * once restored.<br/>
-     * This functions helps to identify and destroy those entities.
+     * This function helps to identify and destroy those entities.
      *
      * @return A non-const reference to this loader.
      */
     basic_continuous_loader &orphans() {
-        reg->each([this](const auto entt) {
-            if(reg->orphan(entt)) {
-                reg->release(entt);
-            }
-        });
-
+        internal::orphans(*reg);
         return *this;
     }
 
@@ -532,8 +433,9 @@ public:
      * @param entt A valid identifier.
      * @return True if `entity` is managed by the loader, false otherwise.
      */
-    [[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT {
-        return (remloc.find(entt) != remloc.cend());
+    [[nodiscard]] bool contains(entity_type entt) const noexcept {
+        const auto it = remloc.find(to_entity(entt));
+        return it != remloc.cend() && it->second.first == entt;
     }
 
     /**
@@ -541,20 +443,17 @@ public:
      * @param entt A valid identifier.
      * @return The local identifier if any, the null entity otherwise.
      */
-    [[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT {
-        const auto it = remloc.find(entt);
-        entity_type other = null;
-
-        if(it != remloc.cend()) {
-            other = it->second.first;
+    [[nodiscard]] entity_type map(entity_type entt) const noexcept {
+        if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) {
+            return it->second.second;
         }
 
-        return other;
+        return null;
     }
 
 private:
-    dense_hash_map<entity_type, std::pair<entity_type, bool>> remloc;
-    basic_registry<entity_type> *reg;
+    dense_map<typename traits_type::entity_type, std::pair<entity_type, entity_type>> remloc;
+    registry_type *reg;
 };
 
 } // namespace entt

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 385 - 215
Dependencies/include/entt/entity/sparse_set.hpp


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 287 - 260
Dependencies/include/entt/entity/storage.hpp


+ 0 - 52
Dependencies/include/entt/entity/utility.hpp

@@ -1,52 +0,0 @@
-#ifndef ENTT_ENTITY_UTILITY_HPP
-#define ENTT_ENTITY_UTILITY_HPP
-
-#include "../core/type_traits.hpp"
-
-namespace entt {
-
-/**
- * @brief Alias for exclusion lists.
- * @tparam Type List of types.
- */
-template<typename... Type>
-struct exclude_t: type_list<Type...> {};
-
-/**
- * @brief Variable template for exclusion lists.
- * @tparam Type List of types.
- */
-template<typename... Type>
-inline constexpr exclude_t<Type...> exclude{};
-
-/**
- * @brief Alias for lists of observed components.
- * @tparam Type List of types.
- */
-template<typename... Type>
-struct get_t: type_list<Type...> {};
-
-/**
- * @brief Variable template for lists of observed components.
- * @tparam Type List of types.
- */
-template<typename... Type>
-inline constexpr get_t<Type...> get{};
-
-/**
- * @brief Alias for lists of owned components.
- * @tparam Type List of types.
- */
-template<typename... Type>
-struct owned_t: type_list<Type...> {};
-
-/**
- * @brief Variable template for lists of owned components.
- * @tparam Type List of types.
- */
-template<typename... Type>
-inline constexpr owned_t<Type...> owned{};
-
-} // namespace entt
-
-#endif

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 346 - 294
Dependencies/include/entt/entity/view.hpp


+ 12 - 5
Dependencies/include/entt/entt.hpp

@@ -1,6 +1,9 @@
+// IWYU pragma: begin_exports
+#include "config/config.h"
+#include "config/macro.h"
 #include "config/version.h"
-#include "container/dense_hash_map.hpp"
-#include "container/dense_hash_set.hpp"
+#include "container/dense_map.hpp"
+#include "container/dense_set.hpp"
 #include "core/algorithm.hpp"
 #include "core/any.hpp"
 #include "core/attribute.h"
@@ -21,6 +24,7 @@
 #include "entity/group.hpp"
 #include "entity/handle.hpp"
 #include "entity/helper.hpp"
+#include "entity/mixin.hpp"
 #include "entity/observer.hpp"
 #include "entity/organizer.hpp"
 #include "entity/registry.hpp"
@@ -28,12 +32,14 @@
 #include "entity/snapshot.hpp"
 #include "entity/sparse_set.hpp"
 #include "entity/storage.hpp"
-#include "entity/utility.hpp"
 #include "entity/view.hpp"
+#include "graph/adjacency_matrix.hpp"
+#include "graph/dot.hpp"
+#include "graph/flow.hpp"
 #include "locator/locator.hpp"
 #include "meta/adl_pointer.hpp"
 #include "meta/container.hpp"
-#include "meta/ctx.hpp"
+#include "meta/context.hpp"
 #include "meta/factory.hpp"
 #include "meta/meta.hpp"
 #include "meta/node.hpp"
@@ -49,9 +55,10 @@
 #include "process/process.hpp"
 #include "process/scheduler.hpp"
 #include "resource/cache.hpp"
-#include "resource/handle.hpp"
 #include "resource/loader.hpp"
+#include "resource/resource.hpp"
 #include "signal/delegate.hpp"
 #include "signal/dispatcher.hpp"
 #include "signal/emitter.hpp"
 #include "signal/sigh.hpp"
+// IWYU pragma: end_exports

+ 4 - 0
Dependencies/include/entt/fwd.hpp

@@ -1,7 +1,11 @@
+// IWYU pragma: begin_exports
 #include "container/fwd.hpp"
 #include "core/fwd.hpp"
 #include "entity/fwd.hpp"
+#include "graph/fwd.hpp"
 #include "meta/fwd.hpp"
 #include "poly/fwd.hpp"
+#include "process/fwd.hpp"
 #include "resource/fwd.hpp"
 #include "signal/fwd.hpp"
+// IWYU pragma: end_exports

+ 349 - 0
Dependencies/include/entt/graph/adjacency_matrix.hpp

@@ -0,0 +1,349 @@
+#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP
+#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include "../config/config.h"
+#include "../core/iterator.hpp"
+#include "fwd.hpp"
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename It>
+class edge_iterator {
+    using size_type = std::size_t;
+
+public:
+    using value_type = std::pair<size_type, size_type>;
+    using pointer = input_iterator_pointer<value_type>;
+    using reference = value_type;
+    using difference_type = std::ptrdiff_t;
+    using iterator_category = std::input_iterator_tag;
+    using iterator_concept = std::forward_iterator_tag;
+
+    constexpr edge_iterator() noexcept
+        : it{},
+          vert{},
+          pos{},
+          last{},
+          offset{} {}
+
+    constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept
+        : it{std::move(base)},
+          vert{vertices},
+          pos{from},
+          last{to},
+          offset{step} {
+        for(; pos != last && !it[pos]; pos += offset) {}
+    }
+
+    constexpr edge_iterator &operator++() noexcept {
+        for(pos += offset; pos != last && !it[pos]; pos += offset) {}
+        return *this;
+    }
+
+    constexpr edge_iterator operator++(int) noexcept {
+        edge_iterator orig = *this;
+        return ++(*this), orig;
+    }
+
+    [[nodiscard]] constexpr reference operator*() const noexcept {
+        return *operator->();
+    }
+
+    [[nodiscard]] constexpr pointer operator->() const noexcept {
+        return std::make_pair<size_type>(pos / vert, pos % vert);
+    }
+
+    template<typename Type>
+    friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
+
+private:
+    It it;
+    size_type vert;
+    size_type pos;
+    size_type last;
+    size_type offset{};
+};
+
+template<typename Container>
+[[nodiscard]] inline constexpr bool operator==(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
+    return lhs.pos == rhs.pos;
+}
+
+template<typename Container>
+[[nodiscard]] inline constexpr bool operator!=(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Basic implementation of a directed adjacency matrix.
+ * @tparam Category Either a directed or undirected category tag.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Category, typename Allocator>
+class adjacency_matrix {
+    using alloc_traits = std::allocator_traits<Allocator>;
+    static_assert(std::is_base_of_v<directed_tag, Category>, "Invalid graph category");
+    static_assert(std::is_same_v<typename alloc_traits::value_type, std::size_t>, "Invalid value type");
+    using container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+
+public:
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+    /*! @brief Vertex type. */
+    using vertex_type = size_type;
+    /*! @brief Edge type. */
+    using edge_type = std::pair<vertex_type, vertex_type>;
+    /*! @brief Vertex iterator type. */
+    using vertex_iterator = iota_iterator<vertex_type>;
+    /*! @brief Edge iterator type. */
+    using edge_iterator = internal::edge_iterator<typename container_type::const_iterator>;
+    /*! @brief Out edge iterator type. */
+    using out_edge_iterator = edge_iterator;
+    /*! @brief In edge iterator type. */
+    using in_edge_iterator = edge_iterator;
+    /*! @brief Graph category tag. */
+    using graph_category = Category;
+
+    /*! @brief Default constructor. */
+    adjacency_matrix() noexcept(noexcept(allocator_type{}))
+        : adjacency_matrix{0u} {}
+
+    /**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit adjacency_matrix(const allocator_type &allocator) noexcept
+        : adjacency_matrix{0u, allocator} {}
+
+    /**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied number of vertices.
+     * @param vertices Number of vertices.
+     * @param allocator The allocator to use.
+     */
+    adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{})
+        : matrix{vertices * vertices, allocator},
+          vert{vertices} {}
+
+    /**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+    adjacency_matrix(const adjacency_matrix &other)
+        : adjacency_matrix{other, other.get_allocator()} {}
+
+    /**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+    adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator)
+        : matrix{other.matrix, allocator},
+          vert{other.vert} {}
+
+    /**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+    adjacency_matrix(adjacency_matrix &&other) noexcept
+        : adjacency_matrix{std::move(other), other.get_allocator()} {}
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator)
+        : matrix{std::move(other.matrix), allocator},
+          vert{std::exchange(other.vert, 0u)} {}
+
+    /**
+     * @brief Default copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This container.
+     */
+    adjacency_matrix &operator=(const adjacency_matrix &other) {
+        matrix = other.matrix;
+        vert = other.vert;
+        return *this;
+    }
+
+    /**
+     * @brief Default move assignment operator.
+     * @param other The instance to move from.
+     * @return This container.
+     */
+    adjacency_matrix &operator=(adjacency_matrix &&other) noexcept {
+        matrix = std::move(other.matrix);
+        vert = std::exchange(other.vert, 0u);
+        return *this;
+    }
+
+    /**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return matrix.get_allocator();
+    }
+
+    /*! @brief Clears the adjacency matrix. */
+    void clear() noexcept {
+        matrix.clear();
+        vert = {};
+    }
+
+    /**
+     * @brief Exchanges the contents with those of a given adjacency matrix.
+     * @param other Adjacency matrix to exchange the content with.
+     */
+    void swap(adjacency_matrix &other) {
+        using std::swap;
+        swap(matrix, other.matrix);
+        swap(vert, other.vert);
+    }
+
+    /**
+     * @brief Returns the number of vertices.
+     * @return The number of vertices.
+     */
+    [[nodiscard]] size_type size() const noexcept {
+        return vert;
+    }
+
+    /**
+     * @brief Returns an iterable object to visit all vertices of a matrix.
+     * @return An iterable object to visit all vertices of a matrix.
+     */
+    [[nodiscard]] iterable_adaptor<vertex_iterator> vertices() const noexcept {
+        return {0u, vert};
+    }
+
+    /**
+     * @brief Returns an iterable object to visit all edges of a matrix.
+     * @return An iterable object to visit all edges of a matrix.
+     */
+    [[nodiscard]] iterable_adaptor<edge_iterator> edges() const noexcept {
+        const auto it = matrix.cbegin();
+        const auto sz = matrix.size();
+        return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}};
+    }
+
+    /**
+     * @brief Returns an iterable object to visit all out edges of a vertex.
+     * @param vertex The vertex of which to return all out edges.
+     * @return An iterable object to visit all out edges of a vertex.
+     */
+    [[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
+        const auto it = matrix.cbegin();
+        const auto from = vertex * vert;
+        const auto to = from + vert;
+        return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}};
+    }
+
+    /**
+     * @brief Returns an iterable object to visit all in edges of a vertex.
+     * @param vertex The vertex of which to return all in edges.
+     * @return An iterable object to visit all in edges of a vertex.
+     */
+    [[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
+        const auto it = matrix.cbegin();
+        const auto from = vertex;
+        const auto to = vert * vert + from;
+        return {{it, vert, from, to, vert}, {it, vert, to, to, vert}};
+    }
+
+    /**
+     * @brief Resizes an adjacency matrix.
+     * @param vertices The new number of vertices.
+     */
+    void resize(const size_type vertices) {
+        adjacency_matrix other{vertices, get_allocator()};
+
+        for(auto [lhs, rhs]: edges()) {
+            other.insert(lhs, rhs);
+        }
+
+        other.swap(*this);
+    }
+
+    /**
+     * @brief Inserts an edge into the adjacency matrix, if it does not exist.
+     * @param lhs The left hand vertex of the edge.
+     * @param rhs The right hand vertex of the edge.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+    std::pair<edge_iterator, bool> insert(const vertex_type lhs, const vertex_type rhs) {
+        const auto pos = lhs * vert + rhs;
+
+        if constexpr(std::is_same_v<graph_category, undirected_tag>) {
+            const auto rev = rhs * vert + lhs;
+            ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
+            matrix[rev] = 1u;
+        }
+
+        const auto inserted = !std::exchange(matrix[pos], 1u);
+        return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted};
+    }
+
+    /**
+     * @brief Removes the edge associated with a pair of given vertices.
+     * @param lhs The left hand vertex of the edge.
+     * @param rhs The right hand vertex of the edge.
+     * @return Number of elements removed (either 0 or 1).
+     */
+    size_type erase(const vertex_type lhs, const vertex_type rhs) {
+        const auto pos = lhs * vert + rhs;
+
+        if constexpr(std::is_same_v<graph_category, undirected_tag>) {
+            const auto rev = rhs * vert + lhs;
+            ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
+            matrix[rev] = 0u;
+        }
+
+        return std::exchange(matrix[pos], 0u);
+    }
+
+    /**
+     * @brief Checks if an adjacency matrix contains a given edge.
+     * @param lhs The left hand vertex of the edge.
+     * @param rhs The right hand vertex of the edge.
+     * @return True if there is such an edge, false otherwise.
+     */
+    [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const {
+        const auto pos = lhs * vert + rhs;
+        return pos < matrix.size() && matrix[pos];
+    }
+
+private:
+    container_type matrix;
+    size_type vert;
+};
+
+} // namespace entt
+
+#endif

+ 58 - 0
Dependencies/include/entt/graph/dot.hpp

@@ -0,0 +1,58 @@
+#ifndef ENTT_GRAPH_DOT_HPP
+#define ENTT_GRAPH_DOT_HPP
+
+#include <ostream>
+#include <type_traits>
+#include "fwd.hpp"
+
+namespace entt {
+
+/**
+ * @brief Outputs a graph in dot format.
+ * @tparam Graph Graph type, valid as long as it exposes edges and vertices.
+ * @tparam Writer Vertex decorator type.
+ * @param out A standard output stream.
+ * @param graph The graph to output.
+ * @param writer Vertex decorator object.
+ */
+template<typename Graph, typename Writer>
+void dot(std::ostream &out, const Graph &graph, Writer writer) {
+    static_assert(std::is_base_of_v<directed_tag, typename Graph::graph_category>, "Invalid graph category");
+
+    if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) {
+        out << "graph{";
+    } else {
+        out << "digraph{";
+    }
+
+    for(auto &&vertex: graph.vertices()) {
+        out << vertex << "[";
+        writer(out, vertex);
+        out << "];";
+    }
+
+    for(auto [lhs, rhs]: graph.edges()) {
+        if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) {
+            out << lhs << "--" << rhs << ";";
+        } else {
+            out << lhs << "->" << rhs << ";";
+        }
+    }
+
+    out << "}";
+}
+
+/**
+ * @brief Outputs a graph in dot format.
+ * @tparam Graph Graph type, valid as long as it exposes edges and vertices.
+ * @param out A standard output stream.
+ * @param graph The graph to output.
+ */
+template<typename Graph>
+void dot(std::ostream &out, const Graph &graph) {
+    return dot(out, graph, [](auto &&...) {});
+}
+
+} // namespace entt
+
+#endif

+ 341 - 0
Dependencies/include/entt/graph/flow.hpp

@@ -0,0 +1,341 @@
+#ifndef ENTT_GRAPH_FLOW_HPP
+#define ENTT_GRAPH_FLOW_HPP
+
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include "../config/config.h"
+#include "../container/dense_map.hpp"
+#include "../container/dense_set.hpp"
+#include "../core/compressed_pair.hpp"
+#include "../core/fwd.hpp"
+#include "../core/iterator.hpp"
+#include "../core/utility.hpp"
+#include "adjacency_matrix.hpp"
+#include "fwd.hpp"
+
+namespace entt {
+
+/**
+ * @brief Utility class for creating task graphs.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Allocator>
+class basic_flow {
+    using alloc_traits = std::allocator_traits<Allocator>;
+    static_assert(std::is_same_v<typename alloc_traits::value_type, id_type>, "Invalid value type");
+    using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
+    using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
+    using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
+    using adjacency_matrix_type = adjacency_matrix<directed_tag, typename alloc_traits::template rebind_alloc<std::size_t>>;
+
+    void emplace(const id_type res, const bool is_rw) {
+        ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
+
+        if(!deps.contains(res) && sync_on != vertices.size()) {
+            deps[res].emplace_back(sync_on, true);
+        }
+
+        deps[res].emplace_back(index.first(), is_rw);
+    }
+
+    void setup_graph(adjacency_matrix_type &matrix) const {
+        for(const auto &elem: deps) {
+            const auto last = elem.second.cend();
+            auto it = elem.second.cbegin();
+
+            while(it != last) {
+                if(it->second) {
+                    // rw item
+                    if(auto curr = it++; it != last) {
+                        if(it->second) {
+                            matrix.insert(curr->first, it->first);
+                        } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
+                            for(; it != next; ++it) {
+                                matrix.insert(curr->first, it->first);
+                                matrix.insert(it->first, next->first);
+                            }
+                        } else {
+                            for(; it != next; ++it) {
+                                matrix.insert(curr->first, it->first);
+                            }
+                        }
+                    }
+                } else {
+                    // ro item (first iteration only)
+                    if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
+                        for(; it != next; ++it) {
+                            matrix.insert(it->first, next->first);
+                        }
+                    } else {
+                        it = last;
+                    }
+                }
+            }
+        }
+    }
+
+    void transitive_closure(adjacency_matrix_type &matrix) const {
+        const auto length = matrix.size();
+
+        for(std::size_t vk{}; vk < length; ++vk) {
+            for(std::size_t vi{}; vi < length; ++vi) {
+                for(std::size_t vj{}; vj < length; ++vj) {
+                    if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) {
+                        matrix.insert(vi, vj);
+                    }
+                }
+            }
+        }
+    }
+
+    void transitive_reduction(adjacency_matrix_type &matrix) const {
+        const auto length = matrix.size();
+
+        for(std::size_t vert{}; vert < length; ++vert) {
+            matrix.erase(vert, vert);
+        }
+
+        for(std::size_t vj{}; vj < length; ++vj) {
+            for(std::size_t vi{}; vi < length; ++vi) {
+                if(matrix.contains(vi, vj)) {
+                    for(std::size_t vk{}; vk < length; ++vk) {
+                        if(matrix.contains(vj, vk)) {
+                            matrix.erase(vi, vk);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+public:
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+    /*! @brief Iterable task list. */
+    using iterable = iterable_adaptor<typename task_container_type::const_iterator>;
+    /*! @brief Adjacency matrix type. */
+    using graph_type = adjacency_matrix_type;
+
+    /*! @brief Default constructor. */
+    basic_flow()
+        : basic_flow{allocator_type{}} {}
+
+    /**
+     * @brief Constructs a flow builder with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit basic_flow(const allocator_type &allocator)
+        : index{0u, allocator},
+          vertices{allocator},
+          deps{allocator},
+          sync_on{} {}
+
+    /*! @brief Default copy constructor. */
+    basic_flow(const basic_flow &) = default;
+
+    /**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+    basic_flow(const basic_flow &other, const allocator_type &allocator)
+        : index{other.index.first(), allocator},
+          vertices{other.vertices, allocator},
+          deps{other.deps, allocator},
+          sync_on{other.sync_on} {}
+
+    /*! @brief Default move constructor. */
+    basic_flow(basic_flow &&) noexcept = default;
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    basic_flow(basic_flow &&other, const allocator_type &allocator)
+        : index{other.index.first(), allocator},
+          vertices{std::move(other.vertices), allocator},
+          deps{std::move(other.deps), allocator},
+          sync_on{other.sync_on} {}
+
+    /**
+     * @brief Default copy assignment operator.
+     * @return This flow builder.
+     */
+    basic_flow &operator=(const basic_flow &) = default;
+
+    /**
+     * @brief Default move assignment operator.
+     * @return This flow builder.
+     */
+    basic_flow &operator=(basic_flow &&) noexcept = default;
+
+    /**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return allocator_type{index.second()};
+    }
+
+    /**
+     * @brief Returns the identifier at specified location.
+     * @param pos Position of the identifier to return.
+     * @return The requested identifier.
+     */
+    [[nodiscard]] id_type operator[](const size_type pos) const {
+        return vertices.cbegin()[pos];
+    }
+
+    /*! @brief Clears the flow builder. */
+    void clear() noexcept {
+        index.first() = {};
+        vertices.clear();
+        deps.clear();
+        sync_on = {};
+    }
+
+    /**
+     * @brief Exchanges the contents with those of a given flow builder.
+     * @param other Flow builder to exchange the content with.
+     */
+    void swap(basic_flow &other) {
+        using std::swap;
+        std::swap(index, other.index);
+        std::swap(vertices, other.vertices);
+        std::swap(deps, other.deps);
+        std::swap(sync_on, other.sync_on);
+    }
+
+    /**
+     * @brief Returns the number of tasks.
+     * @return The number of tasks.
+     */
+    [[nodiscard]] size_type size() const noexcept {
+        return vertices.size();
+    }
+
+    /**
+     * @brief Binds a task to a flow builder.
+     * @param value Task identifier.
+     * @return This flow builder.
+     */
+    basic_flow &bind(const id_type value) {
+        sync_on += (sync_on == vertices.size());
+        const auto it = vertices.emplace(value).first;
+        index.first() = size_type(it - vertices.begin());
+        return *this;
+    }
+
+    /**
+     * @brief Turns the current task into a sync point.
+     * @return This flow builder.
+     */
+    basic_flow &sync() {
+        ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
+        sync_on = index.first();
+
+        for(const auto &elem: deps) {
+            elem.second.emplace_back(sync_on, true);
+        }
+
+        return *this;
+    }
+
+    /**
+     * @brief Assigns a resource to the current task with a given access mode.
+     * @param res Resource identifier.
+     * @param is_rw Access mode.
+     * @return This flow builder.
+     */
+    basic_flow &set(const id_type res, bool is_rw = false) {
+        emplace(res, is_rw);
+        return *this;
+    }
+
+    /**
+     * @brief Assigns a read-only resource to the current task.
+     * @param res Resource identifier.
+     * @return This flow builder.
+     */
+    basic_flow &ro(const id_type res) {
+        emplace(res, false);
+        return *this;
+    }
+
+    /**
+     * @brief Assigns a range of read-only resources to the current task.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return This flow builder.
+     */
+    template<typename It>
+    std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
+    ro(It first, It last) {
+        for(; first != last; ++first) {
+            emplace(*first, false);
+        }
+
+        return *this;
+    }
+
+    /**
+     * @brief Assigns a writable resource to the current task.
+     * @param res Resource identifier.
+     * @return This flow builder.
+     */
+    basic_flow &rw(const id_type res) {
+        emplace(res, true);
+        return *this;
+    }
+
+    /**
+     * @brief Assigns a range of writable resources to the current task.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return This flow builder.
+     */
+    template<typename It>
+    std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
+    rw(It first, It last) {
+        for(; first != last; ++first) {
+            emplace(*first, true);
+        }
+
+        return *this;
+    }
+
+    /**
+     * @brief Generates a task graph for the current content.
+     * @return The adjacency matrix of the task graph.
+     */
+    [[nodiscard]] graph_type graph() const {
+        graph_type matrix{vertices.size(), get_allocator()};
+
+        setup_graph(matrix);
+        transitive_closure(matrix);
+        transitive_reduction(matrix);
+
+        return matrix;
+    }
+
+private:
+    compressed_pair<size_type, allocator_type> index;
+    task_container_type vertices;
+    deps_container_type deps;
+    size_type sync_on;
+};
+
+} // namespace entt
+
+#endif

+ 27 - 0
Dependencies/include/entt/graph/fwd.hpp

@@ -0,0 +1,27 @@
+#ifndef ENTT_GRAPH_FWD_HPP
+#define ENTT_GRAPH_FWD_HPP
+
+#include <cstddef>
+#include <memory>
+#include "../core/fwd.hpp"
+
+namespace entt {
+
+/*! @brief Undirected graph category tag. */
+struct directed_tag {};
+
+/*! @brief Directed graph category tag. */
+struct undirected_tag: directed_tag {};
+
+template<typename, typename = std::allocator<std::size_t>>
+class adjacency_matrix;
+
+template<typename = std::allocator<id_type>>
+class basic_flow;
+
+/*! @brief Alias declaration for the most common use case. */
+using flow = basic_flow<>;
+
+} // namespace entt
+
+#endif

+ 90 - 49
Dependencies/include/entt/locator/locator.hpp

@@ -10,95 +10,136 @@ namespace entt {
 /**
  * @brief Service locator, nothing more.
  *
- * A service locator can be used to do what it promises: locate services.<br/>
+ * A service locator is used to do what it promises: locate services.<br/>
  * Usually service locators are tightly bound to the services they expose and
- * thus it's hard to define a general purpose class to do that. This template
- * based implementation tries to fill the gap and to get rid of the burden of
- * defining a different specific locator for each application.
+ * thus it's hard to define a general purpose class to do that. This tiny class
+ * tries to fill the gap and to get rid of the burden of defining a different
+ * specific locator for each application.
  *
- * @tparam Service Type of service managed by the locator.
+ * @note
+ * Users shouldn't retain references to a service. The recommended way is to
+ * retrieve the service implementation currently set each and every time the
+ * need for it arises. The risk is to incur in unexpected behaviors otherwise.
+ *
+ * @tparam Service Service type.
  */
 template<typename Service>
-struct service_locator {
-    /*! @brief Type of service offered. */
-    using service_type = Service;
+class locator final {
+    class service_handle {
+        friend class locator<Service>;
+        std::shared_ptr<Service> value{};
+    };
+
+public:
+    /*! @brief Service type. */
+    using type = Service;
+    /*! @brief Service node type. */
+    using node_type = service_handle;
 
     /*! @brief Default constructor, deleted on purpose. */
-    service_locator() = delete;
+    locator() = delete;
     /*! @brief Default destructor, deleted on purpose. */
-    ~service_locator() = delete;
+    ~locator() = delete;
 
     /**
-     * @brief Tests if a valid service implementation is set.
-     * @return True if the service is set, false otherwise.
+     * @brief Checks whether a service locator contains a value.
+     * @return True if the service locator contains a value, false otherwise.
      */
-    [[nodiscard]] static bool empty() ENTT_NOEXCEPT {
-        return !static_cast<bool>(service);
+    [[nodiscard]] static bool has_value() noexcept {
+        return (service != nullptr);
     }
 
     /**
-     * @brief Returns a weak pointer to a service implementation, if any.
+     * @brief Returns a reference to a valid service, if any.
      *
-     * Clients of a service shouldn't retain references to it. The recommended
-     * way is to retrieve the service implementation currently set each and
-     * every time the need of using it arises. Otherwise users can incur in
-     * unexpected behaviors.
+     * @warning
+     * Invoking this function can result in undefined behavior if the service
+     * hasn't been set yet.
      *
-     * @return A reference to the service implementation currently set, if any.
+     * @return A reference to the service currently set, if any.
      */
-    [[nodiscard]] static std::weak_ptr<Service> get() ENTT_NOEXCEPT {
-        return service;
+    [[nodiscard]] static Service &value() noexcept {
+        ENTT_ASSERT(has_value(), "Service not available");
+        return *service;
     }
 
     /**
-     * @brief Returns a weak reference to a service implementation, if any.
-     *
-     * Clients of a service shouldn't retain references to it. The recommended
-     * way is to retrieve the service implementation currently set each and
-     * every time the need of using it arises. Otherwise users can incur in
-     * unexpected behaviors.
+     * @brief Returns a service if available or sets it from a fallback type.
      *
-     * @warning
-     * In case no service implementation has been set, a call to this function
-     * results in undefined behavior.
+     * Arguments are used only if a service doesn't already exist. In all other
+     * cases, they are discarded.
      *
-     * @return A reference to the service implementation currently set, if any.
+     * @tparam Args Types of arguments to use to construct the fallback service.
+     * @tparam Type Fallback service type.
+     * @param args Parameters to use to construct the fallback service.
+     * @return A reference to a valid service.
      */
-    [[nodiscard]] static Service &ref() ENTT_NOEXCEPT {
-        return *service;
+    template<typename Type = Service, typename... Args>
+    [[nodiscard]] static Service &value_or(Args &&...args) {
+        return service ? *service : emplace<Type>(std::forward<Args>(args)...);
     }
 
     /**
      * @brief Sets or replaces a service.
-     * @tparam Impl Type of the new service to use.
+     * @tparam Type Service type.
      * @tparam Args Types of arguments to use to construct the service.
      * @param args Parameters to use to construct the service.
+     * @return A reference to a valid service.
      */
-    template<typename Impl = Service, typename... Args>
-    static void set(Args &&...args) {
-        service = std::make_shared<Impl>(std::forward<Args>(args)...);
+    template<typename Type = Service, typename... Args>
+    static Service &emplace(Args &&...args) {
+        service = std::make_shared<Type>(std::forward<Args>(args)...);
+        return *service;
     }
 
     /**
-     * @brief Sets or replaces a service.
-     * @param ptr Service to use to replace the current one.
+     * @brief Sets or replaces a service using a given allocator.
+     * @tparam Type Service type.
+     * @tparam Allocator Type of allocator used to manage memory and elements.
+     * @tparam Args Types of arguments to use to construct the service.
+     * @param alloc The allocator to use.
+     * @param args Parameters to use to construct the service.
+     * @return A reference to a valid service.
      */
-    static void set(std::shared_ptr<Service> ptr) {
-        ENTT_ASSERT(static_cast<bool>(ptr), "Null service not allowed");
-        service = std::move(ptr);
+    template<typename Type = Service, typename Allocator, typename... Args>
+    static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) {
+        service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...);
+        return *service;
     }
 
     /**
-     * @brief Resets a service.
-     *
-     * The service is no longer valid after a reset.
+     * @brief Returns a handle to the underlying service.
+     * @return A handle to the underlying service.
+     */
+    static node_type handle() noexcept {
+        node_type node{};
+        node.value = service;
+        return node;
+    }
+
+    /**
+     * @brief Resets or replaces a service.
+     * @param other Optional handle with which to replace the service.
+     */
+    static void reset(const node_type &other = {}) noexcept {
+        service = other.value;
+    }
+
+    /**
+     * @brief Resets or replaces a service.
+     * @tparam Type Service type.
+     * @tparam Deleter Deleter type.
+     * @param elem A pointer to a service to manage.
+     * @param deleter A deleter to use to destroy the service.
      */
-    static void reset() {
-        service.reset();
+    template<typename Type, typename Deleter = std::default_delete<Type>>
+    static void reset(Type *elem, Deleter deleter = {}) {
+        service = std::shared_ptr<Service>{elem, std::move(deleter)};
     }
 
 private:
-    inline static std::shared_ptr<Service> service = nullptr;
+    // std::shared_ptr because of its type erased allocator which is useful here
+    inline static std::shared_ptr<Service> service{};
 };
 
 } // namespace entt

+ 281 - 161
Dependencies/include/entt/meta/container.hpp

@@ -2,14 +2,18 @@
 #define ENTT_META_CONTAINER_HPP
 
 #include <array>
+#include <deque>
+#include <iterator>
+#include <list>
 #include <map>
 #include <set>
 #include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
-#include "../container/dense_hash_map.hpp"
-#include "../container/dense_hash_set.hpp"
+#include "../container/dense_map.hpp"
+#include "../container/dense_set.hpp"
+#include "context.hpp"
 #include "meta.hpp"
 #include "type_traits.hpp"
 
@@ -23,251 +27,367 @@ namespace entt {
 namespace internal {
 
 template<typename, typename = void>
-struct is_dynamic_sequence_container: std::false_type {};
+struct fixed_size_sequence_container: std::true_type {};
 
 template<typename Type>
-struct is_dynamic_sequence_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {};
+struct fixed_size_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::false_type {};
+
+template<typename Type>
+inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container<Type>::value;
 
 template<typename, typename = void>
-struct is_key_only_meta_associative_container: std::true_type {};
+struct key_only_associative_container: std::true_type {};
 
 template<typename Type>
-struct is_key_only_meta_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
+struct key_only_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
 
 template<typename Type>
-struct basic_meta_sequence_container_traits {
-    using iterator = meta_sequence_container::iterator;
-    using size_type = std::size_t;
+inline constexpr bool key_only_associative_container_v = key_only_associative_container<Type>::value;
 
-    [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT {
-        return any_cast<const Type &>(container).size();
-    }
+template<typename, typename = void>
+struct reserve_aware_container: std::false_type {};
 
-    [[nodiscard]] static bool resize([[maybe_unused]] any &container, [[maybe_unused]] size_type sz) {
-        if constexpr(is_dynamic_sequence_container<Type>::value) {
-            if(auto *const cont = any_cast<Type>(&container); cont) {
-                cont->resize(sz);
-                return true;
-            }
-        }
+template<typename Type>
+struct reserve_aware_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {};
 
-        return false;
-    }
+template<typename Type>
+inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>::value;
 
-    [[nodiscard]] static bool clear([[maybe_unused]] any &container) {
-        if constexpr(is_dynamic_sequence_container<Type>::value) {
-            if(auto *const cont = any_cast<Type>(&container); cont) {
-                cont->clear();
-                return true;
-            }
-        }
+} // namespace internal
 
-        return false;
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief General purpose implementation of meta sequence container traits.
+ * @tparam Type Type of underlying sequence container.
+ */
+template<typename Type>
+struct basic_meta_sequence_container_traits {
+    static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
+
+    /*! @brief True in case of key-only containers, false otherwise. */
+    static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
+
+    /*! @brief Unsigned integer type. */
+    using size_type = typename meta_sequence_container::size_type;
+    /*! @brief Meta iterator type. */
+    using iterator = typename meta_sequence_container::iterator;
+
+    /**
+     * @brief Returns the number of elements in a container.
+     * @param container Opaque pointer to a container of the given type.
+     * @return Number of elements.
+     */
+    [[nodiscard]] static size_type size(const void *container) {
+        return static_cast<const Type *>(container)->size();
     }
 
-    [[nodiscard]] static iterator begin(any &container) {
-        if(auto *const cont = any_cast<Type>(&container); cont) {
-            return iterator{std::begin(*cont)};
+    /**
+     * @brief Clears a container.
+     * @param container Opaque pointer to a container of the given type.
+     * @return True in case of success, false otherwise.
+     */
+    [[nodiscard]] static bool clear([[maybe_unused]] void *container) {
+        if constexpr(fixed_size) {
+            return false;
+        } else {
+            static_cast<Type *>(container)->clear();
+            return true;
         }
-
-        return iterator{std::begin(any_cast<const Type &>(container))};
     }
 
-    [[nodiscard]] static iterator end(any &container) {
-        if(auto *const cont = any_cast<Type>(&container); cont) {
-            return iterator{std::end(*cont)};
+    /**
+     * @brief Increases the capacity of a container.
+     * @param container Opaque pointer to a container of the given type.
+     * @param sz Desired capacity.
+     * @return True in case of success, false otherwise.
+     */
+    [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
+        if constexpr(internal::reserve_aware_container_v<Type>) {
+            static_cast<Type *>(container)->reserve(sz);
+            return true;
+        } else {
+            return false;
         }
-
-        return iterator{std::end(any_cast<const Type &>(container))};
     }
 
-    [[nodiscard]] static iterator insert([[maybe_unused]] any &container, [[maybe_unused]] iterator it, [[maybe_unused]] meta_any &value) {
-        if constexpr(is_dynamic_sequence_container<Type>::value) {
-            if(auto *const cont = any_cast<Type>(&container); cont) {
-                // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
-                if(value.allow_cast<typename Type::const_reference>() || value.allow_cast<typename Type::value_type>()) {
-                    const auto *element = value.try_cast<std::remove_reference_t<typename Type::const_reference>>();
-                    return iterator{cont->insert(any_cast<const typename Type::iterator &>(it.base()), element ? *element : value.cast<typename Type::value_type>())};
-                }
-            }
+    /**
+     * @brief Resizes a container.
+     * @param container Opaque pointer to a container of the given type.
+     * @param sz The new number of elements.
+     * @return True in case of success, false otherwise.
+     */
+    [[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
+        if constexpr(fixed_size || !std::is_default_constructible_v<typename Type::value_type>) {
+            return false;
+        } else {
+            static_cast<Type *>(container)->resize(sz);
+            return true;
         }
-
-        return {};
     }
 
-    [[nodiscard]] static iterator erase([[maybe_unused]] any &container, [[maybe_unused]] iterator it) {
-        if constexpr(is_dynamic_sequence_container<Type>::value) {
-            if(auto *const cont = any_cast<Type>(&container); cont) {
-                return iterator{cont->erase(any_cast<const typename Type::iterator &>(it.base()))};
-            }
-        }
+    /**
+     * @brief Returns a possibly const iterator to the beginning.
+     * @param area The context to pass to the newly created iterator.
+     * @param container Opaque pointer to a container of the given type.
+     * @param as_const Const opaque pointer fallback.
+     * @return An iterator to the first element of the container.
+     */
+    static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
+        return container ? iterator{area, static_cast<Type *>(container)->begin()}
+                         : iterator{area, static_cast<const Type *>(as_const)->begin()};
+    }
 
-        return {};
+    /**
+     * @brief Returns a possibly const iterator to the end.
+     * @param area The context to pass to the newly created iterator.
+     * @param container Opaque pointer to a container of the given type.
+     * @param as_const Const opaque pointer fallback.
+     * @return An iterator that is past the last element of the container.
+     */
+    static iterator end(const meta_ctx &area, void *container, const void *as_const) {
+        return container ? iterator{area, static_cast<Type *>(container)->end()}
+                         : iterator{area, static_cast<const Type *>(as_const)->end()};
     }
 
-    [[nodiscard]] static meta_any get(any &container, size_type pos) {
-        if(auto *const cont = any_cast<Type>(&container); cont) {
-            return meta_any{std::in_place_type<typename Type::reference>, (*cont)[pos]};
+    /**
+     * @brief Assigns one element to a container and constructs its object from
+     * a given opaque instance.
+     * @param area The context to pass to the newly created iterator.
+     * @param container Opaque pointer to a container of the given type.
+     * @param value Optional opaque instance of the object to construct (as
+     * value type).
+     * @param cref Optional opaque instance of the object to construct (as
+     * decayed const reference type).
+     * @param it Iterator before which the element will be inserted.
+     * @return A possibly invalid iterator to the inserted element.
+     */
+    [[nodiscard]] static iterator insert(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
+        if constexpr(fixed_size) {
+            return iterator{area};
+        } else {
+            auto *const non_const = any_cast<typename Type::iterator>(&it.base());
+            return {area, static_cast<Type *>(container)->insert(
+                              non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()),
+                              value ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
         }
+    }
 
-        return meta_any{std::in_place_type<typename Type::const_reference>, any_cast<const Type &>(container)[pos]};
+    /**
+     * @brief Erases an element from a container.
+     * @param area The context to pass to the newly created iterator.
+     * @param container Opaque pointer to a container of the given type.
+     * @param it An opaque iterator to the element to erase.
+     * @return A possibly invalid iterator following the last removed element.
+     */
+    [[nodiscard]] static iterator erase(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
+        if constexpr(fixed_size) {
+            return iterator{area};
+        } else {
+            auto *const non_const = any_cast<typename Type::iterator>(&it.base());
+            return {area, static_cast<Type *>(container)->erase(non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()))};
+        }
     }
 };
 
+/**
+ * @brief General purpose implementation of meta associative container traits.
+ * @tparam Type Type of underlying associative container.
+ */
 template<typename Type>
 struct basic_meta_associative_container_traits {
-    using iterator = meta_associative_container::iterator;
-    using size_type = std::size_t;
-
-    static constexpr auto key_only = is_key_only_meta_associative_container<Type>::value;
+    static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
+
+    /*! @brief True in case of key-only containers, false otherwise. */
+    static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
+
+    /*! @brief Unsigned integer type. */
+    using size_type = typename meta_associative_container::size_type;
+    /*! @brief Meta iterator type. */
+    using iterator = typename meta_associative_container::iterator;
+
+    /**
+     * @brief Returns the number of elements in a container.
+     * @param container Opaque pointer to a container of the given type.
+     * @return Number of elements.
+     */
+    [[nodiscard]] static size_type size(const void *container) {
+        return static_cast<const Type *>(container)->size();
+    }
 
-    [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT {
-        return any_cast<const Type &>(container).size();
+    /**
+     * @brief Clears a container.
+     * @param container Opaque pointer to a container of the given type.
+     * @return True in case of success, false otherwise.
+     */
+    [[nodiscard]] static bool clear(void *container) {
+        static_cast<Type *>(container)->clear();
+        return true;
     }
 
-    [[nodiscard]] static bool clear(any &container) {
-        if(auto *const cont = any_cast<Type>(&container); cont) {
-            cont->clear();
+    /**
+     * @brief Increases the capacity of a container.
+     * @param container Opaque pointer to a container of the given type.
+     * @param sz Desired capacity.
+     * @return True in case of success, false otherwise.
+     */
+    [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
+        if constexpr(internal::reserve_aware_container_v<Type>) {
+            static_cast<Type *>(container)->reserve(sz);
             return true;
+        } else {
+            return false;
         }
-
-        return false;
     }
 
-    [[nodiscard]] static iterator begin(any &container) {
-        if(auto *const cont = any_cast<Type>(&container); cont) {
-            return iterator{std::integral_constant<bool, key_only>{}, cont->begin()};
-        }
-
-        return iterator{std::integral_constant<bool, key_only>{}, std::begin(any_cast<const Type &>(container))};
+    /**
+     * @brief Returns a possibly const iterator to the beginning.
+     * @param area The context to pass to the newly created iterator.
+     * @param container Opaque pointer to a container of the given type.
+     * @param as_const Const opaque pointer fallback.
+     * @return An iterator to the first element of the container.
+     */
+    static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
+        return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->begin()}
+                         : iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->begin()};
     }
 
-    [[nodiscard]] static iterator end(any &container) {
-        if(auto *const cont = any_cast<Type>(&container); cont) {
-            return iterator{std::integral_constant<bool, key_only>{}, cont->end()};
-        }
-
-        return iterator{std::integral_constant<bool, key_only>{}, std::end(any_cast<const Type &>(container))};
+    /**
+     * @brief Returns a possibly const iterator to the end.
+     * @param area The context to pass to the newly created iterator.
+     * @param container Opaque pointer to a container of the given type.
+     * @param as_const Const opaque pointer fallback.
+     * @return An iterator that is past the last element of the container.
+     */
+    static iterator end(const meta_ctx &area, void *container, const void *as_const) {
+        return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->end()}
+                         : iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->end()};
     }
 
-    [[nodiscard]] static bool insert(any &container, meta_any &key, [[maybe_unused]] meta_any &value) {
-        if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
-            if constexpr(is_key_only_meta_associative_container<Type>::value) {
-                return cont->insert(key.cast<const typename Type::key_type &>()).second;
-            } else {
-                if(value.allow_cast<const typename Type::mapped_type &>()) {
-                    return cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second;
-                }
-            }
+    /**
+     * @brief Inserts an element into a container, if the key does not exist.
+     * @param container Opaque pointer to a container of the given type.
+     * @param key An opaque key value of an element to insert.
+     * @param value Optional opaque value to insert (key-value containers).
+     * @return True if the insertion took place, false otherwise.
+     */
+    [[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) {
+        if constexpr(key_only) {
+            return static_cast<Type *>(container)->insert(*static_cast<const typename Type::key_type *>(key)).second;
+        } else {
+            return static_cast<Type *>(container)->emplace(*static_cast<const typename Type::key_type *>(key), *static_cast<const typename Type::mapped_type *>(value)).second;
         }
-
-        return false;
     }
 
-    [[nodiscard]] static bool erase(any &container, meta_any &key) {
-        if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
-            return (cont->erase(key.cast<const typename Type::key_type &>()) != cont->size());
-        }
-
-        return false;
+    /**
+     * @brief Removes an element from a container.
+     * @param container Opaque pointer to a container of the given type.
+     * @param key An opaque key value of an element to remove.
+     * @return Number of elements removed (either 0 or 1).
+     */
+    [[nodiscard]] static size_type erase(void *container, const void *key) {
+        return static_cast<Type *>(container)->erase(*static_cast<const typename Type::key_type *>(key));
     }
 
-    [[nodiscard]] static iterator find(any &container, meta_any &key) {
-        if(key.allow_cast<const typename Type::key_type &>()) {
-            if(auto *const cont = any_cast<Type>(&container); cont) {
-                return iterator{std::integral_constant<bool, key_only>{}, cont->find(key.cast<const typename Type::key_type &>())};
-            }
-
-            return iterator{std::integral_constant<bool, key_only>{}, any_cast<const Type &>(container).find(key.cast<const typename Type::key_type &>())};
-        }
-
-        return {};
+    /**
+     * @brief Finds an element with a given key.
+     * @param area The context to pass to the newly created iterator.
+     * @param container Opaque pointer to a container of the given type.
+     * @param as_const Const opaque pointer fallback.
+     * @param key Opaque key value of an element to search for.
+     * @return An iterator to the element with the given key, if any.
+     */
+    static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) {
+        return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const typename Type::key_type *>(key))}
+                         : iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const typename Type::key_type *>(key))};
     }
 };
 
-} // namespace internal
-
-/**
- * Internal details not to be documented.
- * @endcond
- */
-
 /**
  * @brief Meta sequence container traits for `std::vector`s of any type.
- * @tparam Type The type of elements.
- * @tparam Args Other arguments.
+ * @tparam Args Template arguments for the container.
  */
-template<typename Type, typename... Args>
-struct meta_sequence_container_traits<std::vector<Type, Args...>>
-    : internal::basic_meta_sequence_container_traits<std::vector<Type, Args...>> {};
+template<typename... Args>
+struct meta_sequence_container_traits<std::vector<Args...>>
+    : basic_meta_sequence_container_traits<std::vector<Args...>> {};
 
 /**
  * @brief Meta sequence container traits for `std::array`s of any type.
- * @tparam Type The type of elements.
- * @tparam N The number of elements.
+ * @tparam Type Template arguments for the container.
+ * @tparam N Template arguments for the container.
  */
 template<typename Type, auto N>
 struct meta_sequence_container_traits<std::array<Type, N>>
-    : internal::basic_meta_sequence_container_traits<std::array<Type, N>> {};
+    : basic_meta_sequence_container_traits<std::array<Type, N>> {};
+
+/**
+ * @brief Meta sequence container traits for `std::list`s of any type.
+ * @tparam Args Template arguments for the container.
+ */
+template<typename... Args>
+struct meta_sequence_container_traits<std::list<Args...>>
+    : basic_meta_sequence_container_traits<std::list<Args...>> {};
+
+/**
+ * @brief Meta sequence container traits for `std::deque`s of any type.
+ * @tparam Args Template arguments for the container.
+ */
+template<typename... Args>
+struct meta_sequence_container_traits<std::deque<Args...>>
+    : basic_meta_sequence_container_traits<std::deque<Args...>> {};
 
 /**
  * @brief Meta associative container traits for `std::map`s of any type.
- * @tparam Key The key type of elements.
- * @tparam Value The value type of elements.
- * @tparam Args Other arguments.
+ * @tparam Args Template arguments for the container.
  */
-template<typename Key, typename Value, typename... Args>
-struct meta_associative_container_traits<std::map<Key, Value, Args...>>
-    : internal::basic_meta_associative_container_traits<std::map<Key, Value, Args...>> {};
+template<typename... Args>
+struct meta_associative_container_traits<std::map<Args...>>
+    : basic_meta_associative_container_traits<std::map<Args...>> {};
 
 /**
  * @brief Meta associative container traits for `std::unordered_map`s of any
  * type.
- * @tparam Key The key type of elements.
- * @tparam Value The value type of elements.
- * @tparam Args Other arguments.
+ * @tparam Args Template arguments for the container.
  */
-template<typename Key, typename Value, typename... Args>
-struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>>
-    : internal::basic_meta_associative_container_traits<std::unordered_map<Key, Value, Args...>> {};
+template<typename... Args>
+struct meta_associative_container_traits<std::unordered_map<Args...>>
+    : basic_meta_associative_container_traits<std::unordered_map<Args...>> {};
 
 /**
  * @brief Meta associative container traits for `std::set`s of any type.
- * @tparam Key The type of elements.
- * @tparam Args Other arguments.
+ * @tparam Args Template arguments for the container.
  */
-template<typename Key, typename... Args>
-struct meta_associative_container_traits<std::set<Key, Args...>>
-    : internal::basic_meta_associative_container_traits<std::set<Key, Args...>> {};
+template<typename... Args>
+struct meta_associative_container_traits<std::set<Args...>>
+    : basic_meta_associative_container_traits<std::set<Args...>> {};
 
 /**
  * @brief Meta associative container traits for `std::unordered_set`s of any
  * type.
- * @tparam Key The type of elements.
- * @tparam Args Other arguments.
+ * @tparam Args Template arguments for the container.
  */
-template<typename Key, typename... Args>
-struct meta_associative_container_traits<std::unordered_set<Key, Args...>>
-    : internal::basic_meta_associative_container_traits<std::unordered_set<Key, Args...>> {};
+template<typename... Args>
+struct meta_associative_container_traits<std::unordered_set<Args...>>
+    : basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
 
 /**
- * @brief Meta associative container traits for `dense_hash_map`s of any type.
- * @tparam Key The key type of the elements.
- * @tparam Type The value type of the elements.
- * @tparam Args Other arguments.
+ * @brief Meta associative container traits for `dense_map`s of any type.
+ * @tparam Args Template arguments for the container.
  */
-template<typename Key, typename Type, typename... Args>
-struct meta_associative_container_traits<dense_hash_map<Key, Type, Args...>>
-    : internal::basic_meta_associative_container_traits<dense_hash_map<Key, Type, Args...>> {};
+template<typename... Args>
+struct meta_associative_container_traits<dense_map<Args...>>
+    : basic_meta_associative_container_traits<dense_map<Args...>> {};
 
 /**
- * @brief Meta associative container traits for `dense_hash_set`s of any type.
- * @tparam Type The value type of the elements.
- * @tparam Args Other arguments.
+ * @brief Meta associative container traits for `dense_set`s of any type.
+ * @tparam Args Template arguments for the container.
  */
-template<typename Type, typename... Args>
-struct meta_associative_container_traits<dense_hash_set<Type, Args...>>
-    : internal::basic_meta_associative_container_traits<dense_hash_set<Type, Args...>> {};
+template<typename... Args>
+struct meta_associative_container_traits<dense_set<Args...>>
+    : basic_meta_associative_container_traits<dense_set<Args...>> {};
 
 } // namespace entt
 

+ 67 - 0
Dependencies/include/entt/meta/context.hpp

@@ -0,0 +1,67 @@
+#ifndef ENTT_META_CTX_HPP
+#define ENTT_META_CTX_HPP
+
+#include "../container/dense_map.hpp"
+#include "../core/fwd.hpp"
+#include "../core/utility.hpp"
+
+namespace entt {
+
+class meta_ctx;
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct meta_type_node;
+
+struct meta_context {
+    dense_map<id_type, meta_type_node, identity> value{};
+
+    [[nodiscard]] inline static meta_context &from(meta_ctx &ctx);
+    [[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx);
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Disambiguation tag for constructors and the like. */
+class meta_ctx_arg_t final {};
+
+/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */
+inline constexpr meta_ctx_arg_t meta_ctx_arg{};
+
+/*! @brief Opaque meta context type. */
+class meta_ctx: private internal::meta_context {
+    // attorney idiom like model to access the base class
+    friend struct internal::meta_context;
+};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
+    return ctx;
+}
+
+[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
+    return ctx;
+}
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+} // namespace entt
+
+#endif

+ 0 - 57
Dependencies/include/entt/meta/ctx.hpp

@@ -1,57 +0,0 @@
-#ifndef ENTT_META_CTX_HPP
-#define ENTT_META_CTX_HPP
-
-#include "../config/config.h"
-#include "../core/attribute.h"
-
-namespace entt {
-
-/**
- * @cond TURN_OFF_DOXYGEN
- * Internal details not to be documented.
- */
-
-namespace internal {
-
-struct meta_type_node;
-
-struct ENTT_API meta_context {
-    // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++
-    //     inline static meta_type_node *local = nullptr;
-    //     inline static meta_type_node **global = &local;
-
-    [[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT {
-        static meta_type_node *chain = nullptr;
-        return chain;
-    }
-
-    [[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT {
-        static meta_type_node **chain = &local();
-        return chain;
-    }
-};
-
-} // namespace internal
-
-/**
- * Internal details not to be documented.
- * @endcond
- */
-
-/*! @brief Opaque container for a meta context. */
-struct meta_ctx {
-    /**
-     * @brief Binds the meta system to a given context.
-     * @param other A valid context to which to bind.
-     */
-    static void bind(meta_ctx other) ENTT_NOEXCEPT {
-        internal::meta_context::global() = other.ctx;
-    }
-
-private:
-    internal::meta_type_node **ctx{&internal::meta_context::local()};
-};
-
-} // namespace entt
-
-#endif

+ 306 - 376
Dependencies/include/entt/meta/factory.hpp

@@ -1,9 +1,9 @@
 #ifndef ENTT_META_FACTORY_HPP
 #define ENTT_META_FACTORY_HPP
 
-#include <algorithm>
 #include <cstddef>
 #include <functional>
+#include <memory>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -11,219 +11,118 @@
 #include "../core/fwd.hpp"
 #include "../core/type_info.hpp"
 #include "../core/type_traits.hpp"
+#include "../locator/locator.hpp"
+#include "context.hpp"
 #include "meta.hpp"
 #include "node.hpp"
 #include "policy.hpp"
 #include "range.hpp"
+#include "resolve.hpp"
 #include "utility.hpp"
 
 namespace entt {
 
 /**
- * @brief Meta factory to be used for reflection purposes.
- *
- * The meta factory is an utility class used to reflect types, data members and
- * functions of all sorts. This class ensures that the underlying web of types
- * is built correctly and performs some checks in debug mode to ensure that
- * there are no subtle errors at runtime.
- */
-template<typename...>
-class meta_factory;
-
-/**
- * @brief Extended meta factory to be used for reflection purposes.
- * @tparam Type Reflected type for which the factory was created.
- * @tparam Spec Property specialization pack used to disambiguate overloads.
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
  */
-template<typename Type, typename... Spec>
-class meta_factory<Type, Spec...>: public meta_factory<Type> {
-    void link_prop_if_required(internal::meta_prop_node &node) {
-        if(meta_range<internal::meta_prop_node *, internal::meta_prop_node> range{*ref}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
-            ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [&node](const auto *curr) { return curr->id == node.id; }) == range.cend(), "Duplicate identifier");
-            node.next = *ref;
-            *ref = &node;
-        }
-    }
 
-    template<std::size_t Step = 0, typename... Property, typename... Other>
-    void unroll(choice_t<2>, std::tuple<Property...> property, Other &&...other) {
-        std::apply([this](auto &&...curr) { (this->unroll<Step>(choice<2>, std::forward<Property>(curr)...)); }, property);
-        unroll<Step + sizeof...(Property)>(choice<2>, std::forward<Other>(other)...);
-    }
-
-    template<std::size_t Step = 0, typename... Property, typename... Other>
-    void unroll(choice_t<1>, std::pair<Property...> property, Other &&...other) {
-        assign<Step>(std::move(property.first), std::move(property.second));
-        unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
-    }
+namespace internal {
 
-    template<std::size_t Step = 0, typename Property, typename... Other>
-    void unroll(choice_t<0>, Property &&property, Other &&...other) {
-        assign<Step>(std::forward<Property>(property));
-        unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
-    }
-
-    template<std::size_t>
-    void unroll(choice_t<0>) {}
-
-    template<std::size_t = 0>
-    void assign(meta_any key, meta_any value = {}) {
-        static meta_any property[2u]{};
-
-        static internal::meta_prop_node node{
-            nullptr,
-            property[0u],
-            property[1u]
-            // tricks clang-format
-        };
-
-        property[0u] = std::move(key);
-        property[1u] = std::move(value);
-
-        link_prop_if_required(node);
-    }
+[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
+    auto &&context = internal::meta_context::from(ctx);
+    ENTT_ASSERT(context.value.contains(info.hash()), "Type not available");
+    return context.value[info.hash()];
+}
 
-public:
-    /**
-     * @brief Constructs an extended factory from a given node.
-     * @param target The underlying node to which to assign the properties.
-     */
-    meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT
-        : ref{target} {}
+inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) {
+    return parent.details->data.insert_or_assign(id, std::move(node)).first->second;
+}
 
-    /**
-     * @brief Assigns a property to the last meta object created.
-     *
-     * Both the key and the value (if any) must be at least copy constructible.
-     *
-     * @tparam PropertyOrKey Type of the property or property key.
-     * @tparam Value Optional type of the property value.
-     * @param property_or_key Property or property key.
-     * @param value Optional property value.
-     * @return A meta factory for the parent type.
-     */
-    template<typename PropertyOrKey, typename... Value>
-    meta_factory<Type> prop(PropertyOrKey &&property_or_key, Value &&...value) {
-        if constexpr(sizeof...(Value) == 0) {
-            unroll(choice<2>, std::forward<PropertyOrKey>(property_or_key));
-        } else {
-            assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
+inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) {
+    if(auto it = parent.details->func.find(id); it != parent.details->func.end()) {
+        for(auto *curr = &it->second; curr; curr = curr->next.get()) {
+            if(curr->invoke == node.invoke) {
+                node.next = std::move(curr->next);
+                *curr = std::move(node);
+                return *curr;
+            }
         }
 
-        return {};
+        // locally overloaded function
+        node.next = std::make_shared<meta_func_node>(std::move(parent.details->func[id]));
     }
 
-    /**
-     * @brief Assigns properties to the last meta object created.
-     *
-     * Both key and value (if any) must be at least copy constructible.
-     *
-     * @tparam Property Types of the properties.
-     * @param property Properties to assign to the last meta object created.
-     * @return A meta factory for the parent type.
-     */
-    template<typename... Property>
-    meta_factory<Type> props(Property... property) {
-        unroll(choice<2>, std::forward<Property>(property)...);
-        return {};
-    }
+    return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
+}
 
-private:
-    internal::meta_prop_node **ref;
-};
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
 
 /**
  * @brief Basic meta factory to be used for reflection purposes.
  * @tparam Type Reflected type for which the factory was created.
  */
 template<typename Type>
-class meta_factory<Type> {
-    void link_base_if_required(internal::meta_base_node &node) {
-        if(meta_range<internal::meta_base_node *, internal::meta_base_node> range{owner->base}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
-            node.next = owner->base;
-            owner->base = &node;
-        }
-    }
-
-    void link_conv_if_required(internal::meta_conv_node &node) {
-        if(meta_range<internal::meta_conv_node *, internal::meta_conv_node> range{owner->conv}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
-            node.next = owner->conv;
-            owner->conv = &node;
-        }
-    }
-
-    void link_ctor_if_required(internal::meta_ctor_node &node) {
-        if(meta_range<internal::meta_ctor_node *, internal::meta_ctor_node> range{owner->ctor}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
-            node.next = owner->ctor;
-            owner->ctor = &node;
-        }
-    }
-
-    void link_data_if_required(const id_type id, internal::meta_data_node &node) {
-        meta_range<internal::meta_data_node *, internal::meta_data_node> range{owner->data};
-        ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, &node](const auto *curr) { return curr != &node && curr->id == id; }) == range.cend(), "Duplicate identifier");
-        node.id = id;
-
-        if(std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
-            node.next = owner->data;
-            owner->data = &node;
-        }
-    }
-
-    void link_func_if_required(const id_type id, internal::meta_func_node &node) {
-        node.id = id;
-
-        if(meta_range<internal::meta_func_node *, internal::meta_func_node> range{owner->func}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
-            node.next = owner->func;
-            owner->func = &node;
-        }
-    }
-
+class meta_factory {
     template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
-    auto data(const id_type id, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+    void data(const id_type id, std::index_sequence<Index...>) noexcept {
         using data_type = std::invoke_result_t<decltype(Getter), Type &>;
         using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
         static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
 
-        static internal::meta_data_node node{
-            {},
-            nullptr,
-            nullptr,
-            Setter::size,
-            /* this is never static */
-            (std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
-            internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
-            &meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
-            [](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
-            &meta_getter<Type, Getter, Policy>
-            // tricks clang-format
-        };
-
-        link_data_if_required(id, node);
-        return meta_factory<Type, Setter, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
+        auto &&elem = internal::meta_extend(
+            internal::owner(*ctx, *info),
+            id,
+            internal::meta_data_node{
+                /* this is never static */
+                (std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
+                Setter::size,
+                &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
+                &meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
+                +[](meta_handle instance, meta_any value) { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
+                &meta_getter<Type, Getter, Policy>});
+
+        bucket = &elem.prop;
     }
 
 public:
     /*! @brief Default constructor. */
-    meta_factory()
-        : owner{internal::meta_node<Type>::resolve()} {}
+    meta_factory() noexcept
+        : meta_factory{locator<meta_ctx>::value_or()} {}
 
     /**
-     * @brief Makes a meta type _searchable_.
-     * @param id Optional unique identifier.
-     * @return An extended meta factory for the given type.
+     * @brief Context aware constructor.
+     * @param area The context into which to construct meta types.
      */
-    auto type(const id_type id = type_hash<Type>::value()) {
-        meta_range<internal::meta_type_node *, internal::meta_type_node> range{*internal::meta_context::global()};
-        ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, this](const auto *curr) { return curr != owner && curr->id == id; }) == range.cend(), "Duplicate identifier");
-        owner->id = id;
-
-        if(std::find(range.cbegin(), range.cend(), owner) == range.cend()) {
-            owner->next = *internal::meta_context::global();
-            *internal::meta_context::global() = owner;
+    meta_factory(meta_ctx &area) noexcept
+        : ctx{&area},
+          bucket{},
+          info{&type_id<Type>()} {
+        auto &&elem = internal::owner(*ctx, *info);
+
+        if(!elem.details) {
+            elem.details = std::make_shared<internal::meta_type_descriptor>();
         }
 
-        return meta_factory<Type, Type>{&owner->prop};
+        bucket = &elem.details->prop;
+    }
+
+    /**
+     * @brief Assigns a custom unique identifier to a meta type.
+     * @param id A custom unique identifier.
+     * @return A meta factory for the given type.
+     */
+    auto type(const id_type id) noexcept {
+        auto &&elem = internal::owner(*ctx, *info);
+        ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
+        bucket = &elem.details->prop;
+        elem.id = id;
+        return *this;
     }
 
     /**
@@ -235,24 +134,12 @@ public:
      * @return A meta factory for the parent type.
      */
     template<typename Base>
-    auto base() ENTT_NOEXCEPT {
-        static_assert(std::is_base_of_v<Base, Type>, "Invalid base type");
-
-        static internal::meta_base_node node{
-            nullptr,
-            internal::meta_node<Base>::resolve(),
-            [](meta_any other) ENTT_NOEXCEPT -> meta_any {
-                if(auto *ptr = other.data(); ptr) {
-                    return forward_as_meta(*static_cast<Base *>(static_cast<Type *>(ptr)));
-                }
-
-                return forward_as_meta(*static_cast<const Base *>(static_cast<const Type *>(std::as_const(other).data())));
-            }
-            // tricks clang-format
-        };
-
-        link_base_if_required(node);
-        return meta_factory<Type>{};
+    auto base() noexcept {
+        static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
+        auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
+        internal::owner(*ctx, *info).details->base.insert_or_assign(type_id<Base>().hash(), internal::meta_base_node{&internal::resolve<Base>, op});
+        bucket = nullptr;
+        return *this;
     }
 
     /**
@@ -268,18 +155,12 @@ public:
      * @return A meta factory for the parent type.
      */
     template<auto Candidate>
-    auto conv() ENTT_NOEXCEPT {
-        static internal::meta_conv_node node{
-            nullptr,
-            internal::meta_node<std::remove_const_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>>::resolve(),
-            [](const meta_any &instance) -> meta_any {
-                return forward_as_meta(std::invoke(Candidate, *static_cast<const Type *>(instance.data())));
-            }
-            // tricks clang-format
-        };
-
-        link_conv_if_required(node);
-        return meta_factory<Type>{};
+    auto conv() noexcept {
+        using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
+        auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
+        internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
+        bucket = nullptr;
+        return *this;
     }
 
     /**
@@ -292,18 +173,12 @@ public:
      * @return A meta factory for the parent type.
      */
     template<typename To>
-    auto conv() ENTT_NOEXCEPT {
-        static_assert(std::is_convertible_v<Type, To>, "Could not convert to the required type");
-
-        static internal::meta_conv_node node{
-            nullptr,
-            internal::meta_node<std::remove_const_t<std::remove_reference_t<To>>>::resolve(),
-            [](const meta_any &instance) -> meta_any { return forward_as_meta(static_cast<To>(*static_cast<const Type *>(instance.data()))); }
-            // tricks clang-format
-        };
-
-        link_conv_if_required(node);
-        return meta_factory<Type>{};
+    auto conv() noexcept {
+        using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
+        auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
+        internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
+        bucket = nullptr;
+        return *this;
     }
 
     /**
@@ -317,24 +192,16 @@ public:
      *
      * @tparam Candidate The actual function to use as a constructor.
      * @tparam Policy Optional policy (no policy set by default).
-     * @return An extended meta factory for the parent type.
+     * @return A meta factory for the parent type.
      */
     template<auto Candidate, typename Policy = as_is_t>
-    auto ctor() ENTT_NOEXCEPT {
+    auto ctor() noexcept {
         using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
         static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
-        static_assert(std::is_same_v<std::decay_t<typename descriptor::return_type>, Type>, "The function doesn't return an object of the required type");
-
-        static internal::meta_ctor_node node{
-            nullptr,
-            descriptor::args_type::size,
-            &meta_arg<typename descriptor::args_type>,
-            &meta_construct<Type, Candidate, Policy>
-            // tricks clang-format
-        };
-
-        link_ctor_if_required(node);
-        return meta_factory<Type>{};
+        static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
+        internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
+        bucket = nullptr;
+        return *this;
     }
 
     /**
@@ -345,22 +212,18 @@ public:
      * type that can be invoked with parameters whose types are those given.
      *
      * @tparam Args Types of arguments to use to construct an instance.
-     * @return An extended meta factory for the parent type.
+     * @return A meta factory for the parent type.
      */
     template<typename... Args>
-    auto ctor() ENTT_NOEXCEPT {
-        using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
-
-        static internal::meta_ctor_node node{
-            nullptr,
-            descriptor::args_type::size,
-            &meta_arg<typename descriptor::args_type>,
-            &meta_construct<Type, Args...>
-            // tricks clang-format
-        };
-
-        link_ctor_if_required(node);
-        return meta_factory<Type>{};
+    auto ctor() noexcept {
+        // default constructor is already implicitly generated, no need for redundancy
+        if constexpr(sizeof...(Args) != 0u) {
+            using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
+            internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
+        }
+
+        bucket = nullptr;
+        return *this;
     }
 
     /**
@@ -382,10 +245,12 @@ public:
      * @return A meta factory for the parent type.
      */
     template<auto Func>
-    auto dtor() ENTT_NOEXCEPT {
+    auto dtor() noexcept {
         static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
-        owner->dtor = [](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
-        return meta_factory<Type>{};
+        auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
+        internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op};
+        bucket = nullptr;
+        return *this;
     }
 
     /**
@@ -399,48 +264,51 @@ public:
      * @tparam Data The actual variable to attach to the meta type.
      * @tparam Policy Optional policy (no policy set by default).
      * @param id Unique identifier.
-     * @return An extended meta factory for the parent type.
+     * @return A meta factory for the parent type.
      */
     template<auto Data, typename Policy = as_is_t>
-    auto data(const id_type id) ENTT_NOEXCEPT {
+    auto data(const id_type id) noexcept {
         if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
-            using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
-
-            static internal::meta_data_node node{
-                {},
-                nullptr,
-                nullptr,
-                1u,
-                /* this is never static */
-                std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
-                internal::meta_node<std::remove_const_t<data_type>>::resolve(),
-                &meta_arg<type_list<std::remove_const_t<data_type>>>,
-                &meta_setter<Type, Data>,
-                &meta_getter<Type, Data, Policy>
-                // tricks clang-format
-            };
-
-            link_data_if_required(id, node);
-            return meta_factory<Type, std::integral_constant<decltype(Data), Data>, std::integral_constant<decltype(Data), Data>>{&node.prop};
+            using data_type = std::invoke_result_t<decltype(Data), Type &>;
+            static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
+
+            auto &&elem = internal::meta_extend(
+                internal::owner(*ctx, *info),
+                id,
+                internal::meta_data_node{
+                    /* this is never static */
+                    std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
+                    1u,
+                    &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
+                    &meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
+                    &meta_setter<Type, Data>,
+                    &meta_getter<Type, Data, Policy>});
+
+            bucket = &elem.prop;
         } else {
-            using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
-
-            static internal::meta_data_node node{
-                {},
-                nullptr,
-                nullptr,
-                1u,
-                ((std::is_same_v<Type, std::remove_const_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
-                internal::meta_node<std::remove_const_t<data_type>>::resolve(),
-                &meta_arg<type_list<std::remove_const_t<data_type>>>,
-                &meta_setter<Type, Data>,
-                &meta_getter<Type, Data, Policy>
-                // tricks clang-format
-            };
-
-            link_data_if_required(id, node);
-            return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&node.prop};
+            using data_type = std::remove_pointer_t<decltype(Data)>;
+
+            if constexpr(std::is_pointer_v<decltype(Data)>) {
+                static_assert(Policy::template value<decltype(*Data)>, "Invalid return type for the given policy");
+            } else {
+                static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
+            }
+
+            auto &&elem = internal::meta_extend(
+                internal::owner(*ctx, *info),
+                id,
+                internal::meta_data_node{
+                    ((std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<data_type>>> || std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
+                    1u,
+                    &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
+                    &meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
+                    &meta_setter<Type, Data>,
+                    &meta_getter<Type, Data, Policy>});
+
+            bucket = &elem.prop;
         }
+
+        return *this;
     }
 
     /**
@@ -461,50 +329,46 @@ public:
      * @tparam Getter The actual function to use as a getter.
      * @tparam Policy Optional policy (no policy set by default).
      * @param id Unique identifier.
-     * @return An extended meta factory for the parent type.
+     * @return A meta factory for the parent type.
      */
     template<auto Setter, auto Getter, typename Policy = as_is_t>
-    auto data(const id_type id) ENTT_NOEXCEPT {
+    auto data(const id_type id) noexcept {
         using data_type = std::invoke_result_t<decltype(Getter), Type &>;
         static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
 
         if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
-            static internal::meta_data_node node{
-                {},
-                nullptr,
-                nullptr,
-                0u,
-                /* this is never static */
-                internal::meta_traits::is_const,
-                internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
-                &meta_arg<type_list<>>,
-                &meta_setter<Type, Setter>,
-                &meta_getter<Type, Getter, Policy>
-                // tricks clang-format
-            };
-
-            link_data_if_required(id, node);
-            return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
+            auto &&elem = internal::meta_extend(
+                internal::owner(*ctx, *info),
+                id,
+                internal::meta_data_node{
+                    /* this is never static */
+                    internal::meta_traits::is_const,
+                    0u,
+                    &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
+                    &meta_arg<type_list<>>,
+                    &meta_setter<Type, Setter>,
+                    &meta_getter<Type, Getter, Policy>});
+
+            bucket = &elem.prop;
         } else {
             using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
 
-            static internal::meta_data_node node{
-                {},
-                nullptr,
-                nullptr,
-                1u,
-                /* this is never static nor const */
-                internal::meta_traits::is_none,
-                internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
-                &meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
-                &meta_setter<Type, Setter>,
-                &meta_getter<Type, Getter, Policy>
-                // tricks clang-format
-            };
-
-            link_data_if_required(id, node);
-            return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
+            auto &&elem = internal::meta_extend(
+                internal::owner(*ctx, *info),
+                id,
+                internal::meta_data_node{
+                    /* this is never static nor const */
+                    internal::meta_traits::is_none,
+                    1u,
+                    &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
+                    &meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
+                    &meta_setter<Type, Setter>,
+                    &meta_getter<Type, Getter, Policy>});
+
+            bucket = &elem.prop;
         }
+
+        return *this;
     }
 
     /**
@@ -522,15 +386,16 @@ public:
      * @tparam Getter The actual getter function.
      * @tparam Policy Optional policy (no policy set by default).
      * @param id Unique identifier.
-     * @return An extended meta factory for the parent type.
+     * @return A meta factory for the parent type.
      */
     template<typename Setter, auto Getter, typename Policy = as_is_t>
-    auto data(const id_type id) ENTT_NOEXCEPT {
-        return data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
+    auto data(const id_type id) noexcept {
+        data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
+        return *this;
     }
 
     /**
-     * @brief Assigns a meta funcion to a meta type.
+     * @brief Assigns a meta function to a meta type.
      *
      * Both member functions and free functions can be assigned to a meta
      * type.<br/>
@@ -540,31 +405,56 @@ public:
      * @tparam Candidate The actual function to attach to the meta type.
      * @tparam Policy Optional policy (no policy set by default).
      * @param id Unique identifier.
-     * @return An extended meta factory for the parent type.
+     * @return A meta factory for the parent type.
      */
     template<auto Candidate, typename Policy = as_is_t>
-    auto func(const id_type id) ENTT_NOEXCEPT {
+    auto func(const id_type id) noexcept {
         using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
         static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
 
-        static internal::meta_func_node node{
-            {},
-            nullptr,
-            nullptr,
-            descriptor::args_type::size,
-            (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
-            internal::meta_node<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>>>::resolve(),
-            &meta_arg<typename descriptor::args_type>,
-            &meta_invoke<Type, Candidate, Policy>
-            // tricks clang-format
-        };
-
-        link_func_if_required(id, node);
-        return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
+        auto &&elem = internal::meta_extend(
+            internal::owner(*ctx, *info),
+            id,
+            internal::meta_func_node{
+                (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
+                descriptor::args_type::size,
+                &internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>,
+                &meta_arg<typename descriptor::args_type>,
+                &meta_invoke<Type, Candidate, Policy>});
+
+        bucket = &elem.prop;
+        return *this;
+    }
+
+    /**
+     * @brief Assigns a property to the last meta object created.
+     *
+     * Both the key and the value (if any) must be at least copy constructible.
+     *
+     * @tparam Value Optional type of the property value.
+     * @param id Property key.
+     * @param value Optional property value.
+     * @return A meta factory for the parent type.
+     */
+    template<typename... Value>
+    meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
+        ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties");
+
+        if constexpr(sizeof...(Value) == 0u) {
+            (*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>};
+        } else {
+            (*bucket)[id] = internal::meta_prop_node{
+                &internal::resolve<std::decay_t<Value>>...,
+                std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...};
+        }
+
+        return *this;
     }
 
 private:
-    internal::meta_type_node *owner;
+    meta_ctx *ctx;
+    dense_map<id_type, internal::meta_prop_node, identity> *bucket;
+    const type_info *info;
 };
 
 /**
@@ -576,13 +466,31 @@ private:
  * dedicated factory.
  *
  * @tparam Type Type to reflect.
+ * @param ctx The context into which to construct meta types.
  * @return A meta factory for the given type.
  */
 template<typename Type>
-[[nodiscard]] auto meta() ENTT_NOEXCEPT {
-    auto *const node = internal::meta_node<Type>::resolve();
-    // extended meta factory to allow assigning properties to opaque meta types
-    return meta_factory<Type, Type>{&node->prop};
+[[nodiscard]] auto meta(meta_ctx &ctx) noexcept {
+    auto &&context = internal::meta_context::from(ctx);
+    // make sure the type exists in the context before returning a factory
+    context.value.try_emplace(type_id<Type>().hash(), internal::resolve<Type>(context));
+    return meta_factory<Type>{ctx};
+}
+
+/**
+ * @brief Utility function to use for reflection.
+ *
+ * This is the point from which everything starts.<br/>
+ * By invoking this function with a type that is not yet reflected, a meta type
+ * is created to which it will be possible to attach meta objects through a
+ * dedicated factory.
+ *
+ * @tparam Type Type to reflect.
+ * @return A meta factory for the given type.
+ */
+template<typename Type>
+[[nodiscard]] auto meta() noexcept {
+    return meta<Type>(locator<meta_ctx>::value_or());
 }
 
 /**
@@ -592,59 +500,81 @@ template<typename Type>
  * well as its constructors, destructors and conversion functions if any.<br/>
  * Base classes aren't reset but the link between the two types is removed.
  *
- * The type is also removed from the list of searchable types.
+ * The type is also removed from the set of searchable types.
  *
  * @param id Unique identifier.
+ * @param ctx The context from which to reset meta types.
  */
-inline void meta_reset(const id_type id) ENTT_NOEXCEPT {
-    auto clear_chain = [](auto **curr, auto... member) {
-        for(; *curr; *curr = std::exchange((*curr)->next, nullptr)) {
-            if constexpr(sizeof...(member) != 0u) {
-                static_assert(sizeof...(member) == 1u, "Assert in defense of the future me");
-                for(auto **sub = (&((*curr)->*member), ...); *sub; *sub = std::exchange((*sub)->next, nullptr)) {}
-            }
-        }
-    };
-
-    for(auto **it = internal::meta_context::global(); *it; it = &(*it)->next) {
-        if(auto *node = *it; node->id == id) {
-            clear_chain(&node->prop);
-            clear_chain(&node->base);
-            clear_chain(&node->conv);
-            clear_chain(&node->ctor);
-            clear_chain(&node->data, &internal::meta_data_node::prop);
-            clear_chain(&node->func, &internal::meta_func_node::prop);
-
-            node->id = {};
-            node->dtor = nullptr;
-            *it = std::exchange(node->next, nullptr);
-
-            break;
+inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept {
+    auto &&context = internal::meta_context::from(ctx);
+
+    for(auto it = context.value.begin(); it != context.value.end();) {
+        if(it->second.id == id) {
+            it = context.value.erase(it);
+        } else {
+            ++it;
         }
     }
 }
 
+/**
+ * @brief Resets a type and all its parts.
+ *
+ * Resets a type and all its data members, member functions and properties, as
+ * well as its constructors, destructors and conversion functions if any.<br/>
+ * Base classes aren't reset but the link between the two types is removed.
+ *
+ * The type is also removed from the set of searchable types.
+ *
+ * @param id Unique identifier.
+ */
+inline void meta_reset(const id_type id) noexcept {
+    meta_reset(locator<meta_ctx>::value_or(), id);
+}
+
 /**
  * @brief Resets a type and all its parts.
  *
  * @sa meta_reset
  *
  * @tparam Type Type to reset.
+ * @param ctx The context from which to reset meta types.
  */
 template<typename Type>
-void meta_reset() ENTT_NOEXCEPT {
-    meta_reset(internal::meta_node<Type>::resolve()->id);
+void meta_reset(meta_ctx &ctx) noexcept {
+    internal::meta_context::from(ctx).value.erase(type_id<Type>().hash());
 }
 
 /**
- * @brief Resets all searchable types.
+ * @brief Resets a type and all its parts.
  *
  * @sa meta_reset
+ *
+ * @tparam Type Type to reset.
  */
-inline void meta_reset() ENTT_NOEXCEPT {
-    while(*internal::meta_context::global()) {
-        meta_reset((*internal::meta_context::global())->id);
-    }
+template<typename Type>
+void meta_reset() noexcept {
+    meta_reset<Type>(locator<meta_ctx>::value_or());
+}
+
+/**
+ * @brief Resets all meta types.
+ *
+ * @sa meta_reset
+ *
+ * @param ctx The context from which to reset meta types.
+ */
+inline void meta_reset(meta_ctx &ctx) noexcept {
+    internal::meta_context::from(ctx).value.clear();
+}
+
+/**
+ * @brief Resets all meta types.
+ *
+ * @sa meta_reset
+ */
+inline void meta_reset() noexcept {
+    meta_reset(locator<meta_ctx>::value_or());
 }
 
 } // namespace entt

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 400 - 303
Dependencies/include/entt/meta/meta.hpp


+ 181 - 138
Dependencies/include/entt/meta/node.hpp

@@ -2,14 +2,18 @@
 #define ENTT_META_NODE_HPP
 
 #include <cstddef>
+#include <memory>
 #include <type_traits>
 #include <utility>
 #include "../config/config.h"
+#include "../container/dense_map.hpp"
 #include "../core/attribute.h"
 #include "../core/enum.hpp"
 #include "../core/fwd.hpp"
 #include "../core/type_info.hpp"
 #include "../core/type_traits.hpp"
+#include "../core/utility.hpp"
+#include "context.hpp"
 #include "type_traits.hpp"
 
 namespace entt {
@@ -30,197 +34,236 @@ enum class meta_traits : std::uint32_t {
     is_const = 0x0001,
     is_static = 0x0002,
     is_arithmetic = 0x0004,
-    is_array = 0x0008,
-    is_enum = 0x0010,
-    is_class = 0x0020,
-    is_pointer = 0x0040,
-    is_meta_pointer_like = 0x0080,
-    is_meta_sequence_container = 0x0100,
-    is_meta_associative_container = 0x0200,
+    is_integral = 0x0008,
+    is_signed = 0x0010,
+    is_array = 0x0020,
+    is_enum = 0x0040,
+    is_class = 0x0080,
+    is_meta_pointer_like = 0x0100,
+    is_meta_sequence_container = 0x0200,
+    is_meta_associative_container = 0x0400,
     _entt_enum_as_bitmask
 };
 
 struct meta_type_node;
 
 struct meta_prop_node {
-    meta_prop_node *next;
-    const meta_any &id;
-    meta_any &value;
+    meta_type_node (*type)(const meta_context &) noexcept {};
+    std::shared_ptr<void> value{};
 };
 
 struct meta_base_node {
-    meta_base_node *next;
-    meta_type_node *const type;
-    meta_any (*const cast)(meta_any) ENTT_NOEXCEPT;
+    meta_type_node (*type)(const meta_context &) noexcept {};
+    const void *(*cast)(const void *) noexcept {};
 };
 
 struct meta_conv_node {
-    meta_conv_node *next;
-    meta_type_node *const type;
-    meta_any (*const conv)(const meta_any &);
+    meta_any (*conv)(const meta_ctx &, const void *){};
 };
 
 struct meta_ctor_node {
     using size_type = std::size_t;
-    meta_ctor_node *next;
-    const size_type arity;
-    meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
-    meta_any (*const invoke)(meta_any *const);
+
+    size_type arity{0u};
+    meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
+    meta_any (*invoke)(const meta_ctx &, meta_any *const){};
+};
+
+struct meta_dtor_node {
+    void (*dtor)(void *){};
 };
 
 struct meta_data_node {
     using size_type = std::size_t;
-    id_type id;
-    meta_data_node *next;
-    meta_prop_node *prop;
-    const size_type arity;
-    const meta_traits traits;
-    meta_type_node *const type;
-    meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
-    bool (*const set)(meta_handle, meta_any);
-    meta_any (*const get)(meta_handle);
+
+    meta_traits traits{meta_traits::is_none};
+    size_type arity{0u};
+    meta_type_node (*type)(const meta_context &) noexcept {};
+    meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
+    bool (*set)(meta_handle, meta_any){};
+    meta_any (*get)(const meta_ctx &, meta_handle){};
+    dense_map<id_type, meta_prop_node, identity> prop{};
 };
 
 struct meta_func_node {
     using size_type = std::size_t;
-    id_type id;
-    meta_func_node *next;
-    meta_prop_node *prop;
-    const size_type arity;
-    const meta_traits traits;
-    meta_type_node *const ret;
-    meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
-    meta_any (*const invoke)(meta_handle, meta_any *const);
+
+    meta_traits traits{meta_traits::is_none};
+    size_type arity{0u};
+    meta_type_node (*ret)(const meta_context &) noexcept {};
+    meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
+    meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){};
+    std::shared_ptr<meta_func_node> next{};
+    dense_map<id_type, meta_prop_node, identity> prop{};
 };
 
 struct meta_template_node {
     using size_type = std::size_t;
-    const size_type arity;
-    meta_type_node *const type;
-    meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT;
+
+    size_type arity{0u};
+    meta_type_node (*type)(const meta_context &) noexcept {};
+    meta_type_node (*arg)(const meta_context &, const size_type) noexcept {};
+};
+
+struct meta_type_descriptor {
+    dense_map<id_type, meta_ctor_node, identity> ctor{};
+    dense_map<id_type, meta_base_node, identity> base{};
+    dense_map<id_type, meta_conv_node, identity> conv{};
+    dense_map<id_type, meta_data_node, identity> data{};
+    dense_map<id_type, meta_func_node, identity> func{};
+    dense_map<id_type, meta_prop_node, identity> prop{};
 };
 
 struct meta_type_node {
     using size_type = std::size_t;
-    const type_info *info;
-    id_type id;
-    meta_type_node *next;
-    meta_prop_node *prop;
-    const size_type size_of;
-    const meta_traits traits;
-    meta_any (*const default_constructor)();
-    double (*const conversion_helper)(void *, const void *);
-    const meta_template_node *const templ;
-    meta_ctor_node *ctor{nullptr};
-    meta_base_node *base{nullptr};
-    meta_conv_node *conv{nullptr};
-    meta_data_node *data{nullptr};
-    meta_func_node *func{nullptr};
-    void (*dtor)(void *){nullptr};
+
+    const type_info *info{};
+    id_type id{};
+    meta_traits traits{meta_traits::is_none};
+    size_type size_of{0u};
+    meta_type_node (*resolve)(const meta_context &) noexcept {};
+    meta_type_node (*remove_pointer)(const meta_context &) noexcept {};
+    meta_any (*default_constructor)(const meta_ctx &){};
+    double (*conversion_helper)(void *, const void *){};
+    meta_any (*from_void)(const meta_ctx &, void *, const void *){};
+    meta_template_node templ{};
+    meta_dtor_node dtor{};
+    std::shared_ptr<meta_type_descriptor> details{};
 };
 
-template<typename... Args>
-meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT;
+template<auto Member>
+auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
+    if(node.details) {
+        if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) {
+            return &it->second;
+        }
 
-template<typename Type>
-class ENTT_API meta_node {
-    static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
-
-    [[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT {
-        if constexpr(std::is_default_constructible_v<Type>) {
-            return +[]() { return meta_any{std::in_place_type<Type>}; };
-        } else {
-            return static_cast<std::decay_t<decltype(meta_type_node::default_constructor)>>(nullptr);
+        for(auto &&curr: node.details->base) {
+            if(auto *elem = look_for<Member>(context, curr.second.type(context), id); elem) {
+                return elem;
+            }
         }
     }
 
-    [[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT {
-        if constexpr(std::is_arithmetic_v<Type>) {
-            return +[](void *bin, const void *value) {
-                return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value));
-            };
-        } else if constexpr(std::is_enum_v<Type>) {
-            return +[](void *bin, const void *value) {
-                return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value));
-            };
-        } else {
-            return static_cast<std::decay_t<decltype(meta_type_node::conversion_helper)>>(nullptr);
-        }
+    return static_cast<typename std::remove_reference_t<decltype(node.details.get()->*Member)>::mapped_type *>(nullptr);
+}
+
+template<typename Type>
+meta_type_node resolve(const meta_context &) noexcept;
+
+template<typename... Args>
+[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list<Args...>, [[maybe_unused]] const std::size_t index) noexcept {
+    [[maybe_unused]] std::size_t pos{};
+    meta_type_node (*value)(const meta_context &) noexcept = nullptr;
+    ((value = (pos++ == index ? &resolve<std::remove_cv_t<std::remove_reference_t<Args>>> : value)), ...);
+    ENTT_ASSERT(value != nullptr, "Out of bounds");
+    return value(context);
+}
+
+[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept {
+    if(from.info && to.info && *from.info == *to.info) {
+        return instance;
     }
 
-    [[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT {
-        if constexpr(is_complete_v<meta_template_traits<Type>>) {
-            static meta_template_node node{
-                meta_template_traits<Type>::args_type::size,
-                meta_node<typename meta_template_traits<Type>::class_type>::resolve(),
-                [](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index); }
-                // tricks clang-format
-            };
-
-            return &node;
-        } else {
-            return nullptr;
+    if(from.details) {
+        for(auto &&curr: from.details->base) {
+            if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) {
+                return elem;
+            }
         }
     }
 
-public:
-    [[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT {
-        static meta_type_node node{
-            &type_id<Type>(),
-            {},
-            nullptr,
-            nullptr,
-            size_of_v<Type>,
-            internal::meta_traits::is_none
-                | (std::is_arithmetic_v<Type> ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none)
-                | (std::is_array_v<Type> ? internal::meta_traits::is_array : internal::meta_traits::is_none)
-                | (std::is_enum_v<Type> ? internal::meta_traits::is_enum : internal::meta_traits::is_none)
-                | (std::is_class_v<Type> ? internal::meta_traits::is_class : internal::meta_traits::is_none)
-                | (std::is_pointer_v<Type> ? internal::meta_traits::is_pointer : internal::meta_traits::is_none)
-                | (is_meta_pointer_like_v<Type> ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none)
-                | (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none)
-                | (is_complete_v<meta_associative_container_traits<Type>> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none),
-            meta_default_constructor(),
-            meta_conversion_helper(),
-            meta_template_info()
-            // tricks clang-format
-        };
+    return nullptr;
+}
 
-        return &node;
+template<typename Func>
+[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) {
+    if(from.info && *from.info == to) {
+        return func(instance, from);
     }
-};
 
-template<typename... Args>
-[[nodiscard]] meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
-    meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node<std::remove_const_t<std::remove_reference_t<Args>>>::resolve()...};
-    return args[index + 1u];
-}
+    if(from.details) {
+        if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) {
+            return func(instance, it->second);
+        }
 
-template<auto Member, typename Type>
-[[nodiscard]] static std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) {
-    for(auto *curr = node->*Member; curr; curr = curr->next) {
-        if constexpr(std::is_same_v<Type, type_info>) {
-            if(*curr->type->info == info_or_id) {
-                return curr;
-            }
-        } else if constexpr(std::is_same_v<decltype(curr), meta_base_node *>) {
-            if(curr->type->id == info_or_id) {
-                return curr;
-            }
-        } else {
-            if(curr->id == info_or_id) {
-                return curr;
+        for(auto &&curr: from.details->base) {
+            if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) {
+                return other;
             }
         }
     }
 
-    for(auto *curr = node->base; curr; curr = curr->next) {
-        if(auto *ret = find_by<Member>(info_or_id, curr->type); ret) {
-            return ret;
-        }
+    if(from.conversion_helper && arithmetic_or_enum) {
+        return func(instance, from.conversion_helper);
     }
 
-    return nullptr;
+    return func(instance);
+}
+
+[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept {
+    const auto it = context.value.find(info.hash());
+    return it != context.value.end() ? &it->second : nullptr;
+}
+
+template<typename Type>
+[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept {
+    static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Invalid type");
+
+    if(auto *elem = try_resolve(context, type_id<Type>()); elem) {
+        return *elem;
+    }
+
+    meta_type_node node{
+        &type_id<Type>(),
+        type_id<Type>().hash(),
+        (std::is_arithmetic_v<Type> ? meta_traits::is_arithmetic : meta_traits::is_none)
+            | (std::is_integral_v<Type> ? meta_traits::is_integral : meta_traits::is_none)
+            | (std::is_signed_v<Type> ? meta_traits::is_signed : meta_traits::is_none)
+            | (std::is_array_v<Type> ? meta_traits::is_array : meta_traits::is_none)
+            | (std::is_enum_v<Type> ? meta_traits::is_enum : meta_traits::is_none)
+            | (std::is_class_v<Type> ? meta_traits::is_class : meta_traits::is_none)
+            | (is_meta_pointer_like_v<Type> ? meta_traits::is_meta_pointer_like : meta_traits::is_none)
+            | (is_complete_v<meta_sequence_container_traits<Type>> ? meta_traits::is_meta_sequence_container : meta_traits::is_none)
+            | (is_complete_v<meta_associative_container_traits<Type>> ? meta_traits::is_meta_associative_container : meta_traits::is_none),
+        size_of_v<Type>,
+        &resolve<Type>,
+        &resolve<std::remove_cv_t<std::remove_pointer_t<Type>>>};
+
+    if constexpr(std::is_default_constructible_v<Type>) {
+        node.default_constructor = +[](const meta_ctx &ctx) {
+            return meta_any{ctx, std::in_place_type<Type>};
+        };
+    }
+
+    if constexpr(std::is_arithmetic_v<Type>) {
+        node.conversion_helper = +[](void *bin, const void *value) {
+            return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value));
+        };
+    } else if constexpr(std::is_enum_v<Type>) {
+        node.conversion_helper = +[](void *bin, const void *value) {
+            return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value));
+        };
+    }
+
+    if constexpr(!std::is_void_v<Type> && !std::is_function_v<Type>) {
+        node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) {
+            if(element) {
+                return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(element)};
+            }
+
+            return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(as_const)};
+        };
+    }
+
+    if constexpr(is_complete_v<meta_template_traits<Type>>) {
+        node.templ = meta_template_node{
+            meta_template_traits<Type>::args_type::size,
+            &resolve<typename meta_template_traits<Type>::class_type>,
+            +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits<Type>::args_type{}, index); }};
+    }
+
+    return node;
 }
 
 } // namespace internal

+ 20 - 4
Dependencies/include/entt/meta/policy.hpp

@@ -6,7 +6,7 @@
 namespace entt {
 
 /*! @brief Empty class type used to request the _as ref_ policy. */
-struct as_ref_t {
+struct as_ref_t final {
     /**
      * @cond TURN_OFF_DOXYGEN
      * Internal details not to be documented.
@@ -20,7 +20,7 @@ struct as_ref_t {
 };
 
 /*! @brief Empty class type used to request the _as cref_ policy. */
-struct as_cref_t {
+struct as_cref_t final {
     /**
      * @cond TURN_OFF_DOXYGEN
      * Internal details not to be documented.
@@ -34,7 +34,7 @@ struct as_cref_t {
 };
 
 /*! @brief Empty class type used to request the _as-is_ policy. */
-struct as_is_t {
+struct as_is_t final {
     /**
      * @cond TURN_OFF_DOXYGEN
      * Internal details not to be documented.
@@ -48,7 +48,7 @@ struct as_is_t {
 };
 
 /*! @brief Empty class type used to request the _as void_ policy. */
-struct as_void_t {
+struct as_void_t final {
     /**
      * @cond TURN_OFF_DOXYGEN
      * Internal details not to be documented.
@@ -61,6 +61,22 @@ struct as_void_t {
      */
 };
 
+/**
+ * @brief Provides the member constant `value` to true if a type also is a meta
+ * policy, false otherwise.
+ * @tparam Type Type to check.
+ */
+template<typename Type>
+struct is_meta_policy
+    : std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type Type to check.
+ */
+template<typename Type>
+inline constexpr bool is_meta_policy_v = is_meta_policy<Type>::value;
+
 } // namespace entt
 
 #endif

+ 96 - 69
Dependencies/include/entt/meta/range.hpp

@@ -3,7 +3,10 @@
 
 #include <cstddef>
 #include <iterator>
+#include <utility>
+#include "../core/fwd.hpp"
 #include "../core/iterator.hpp"
+#include "context.hpp"
 
 namespace entt {
 
@@ -14,49 +17,120 @@ namespace entt {
 
 namespace internal {
 
-template<typename Type, typename Node>
-struct meta_range_iterator {
+template<typename Type, typename It>
+struct meta_range_iterator final {
     using difference_type = std::ptrdiff_t;
-    using value_type = Type;
+    using value_type = std::pair<id_type, Type>;
     using pointer = input_iterator_pointer<value_type>;
     using reference = value_type;
     using iterator_category = std::input_iterator_tag;
-    using node_type = Node;
+    using iterator_concept = std::random_access_iterator_tag;
 
-    meta_range_iterator() ENTT_NOEXCEPT = default;
+    constexpr meta_range_iterator() noexcept
+        : it{},
+          ctx{} {}
 
-    meta_range_iterator(node_type *head) ENTT_NOEXCEPT
-        : it{head} {}
+    constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept
+        : it{iter},
+          ctx{&area} {}
 
-    meta_range_iterator &operator++() ENTT_NOEXCEPT {
-        return (it = it->next), *this;
+    constexpr meta_range_iterator &operator++() noexcept {
+        return ++it, *this;
     }
 
-    meta_range_iterator operator++(int) ENTT_NOEXCEPT {
+    constexpr meta_range_iterator operator++(int) noexcept {
         meta_range_iterator orig = *this;
         return ++(*this), orig;
     }
 
-    [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
-        return it;
+    constexpr meta_range_iterator &operator--() noexcept {
+        return --it, *this;
     }
 
-    [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
-        return operator*();
+    constexpr meta_range_iterator operator--(int) noexcept {
+        meta_range_iterator orig = *this;
+        return operator--(), orig;
+    }
+
+    constexpr meta_range_iterator &operator+=(const difference_type value) noexcept {
+        it += value;
+        return *this;
+    }
+
+    constexpr meta_range_iterator operator+(const difference_type value) const noexcept {
+        meta_range_iterator copy = *this;
+        return (copy += value);
+    }
+
+    constexpr meta_range_iterator &operator-=(const difference_type value) noexcept {
+        return (*this += -value);
+    }
+
+    constexpr meta_range_iterator operator-(const difference_type value) const noexcept {
+        return (*this + -value);
     }
 
-    [[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT {
-        return it == other.it;
+    [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
+        return {it[value].first, Type{*ctx, it[value].second}};
     }
 
-    [[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT {
-        return !(*this == other);
+    [[nodiscard]] constexpr pointer operator->() const noexcept {
+        return operator*();
     }
 
+    [[nodiscard]] constexpr reference operator*() const noexcept {
+        return {it->first, Type{*ctx, it->second}};
+    }
+
+    template<typename... Args>
+    friend constexpr std::ptrdiff_t operator-(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept;
+
+    template<typename... Args>
+    friend constexpr bool operator==(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept;
+
+    template<typename... Args>
+    friend constexpr bool operator<(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept;
+
 private:
-    node_type *it{};
+    It it;
+    const meta_ctx *ctx;
 };
 
+template<typename... Args>
+[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
+    return lhs.it - rhs.it;
+}
+
+template<typename... Args>
+[[nodiscard]] constexpr bool operator==(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
+    return lhs.it == rhs.it;
+}
+
+template<typename... Args>
+[[nodiscard]] constexpr bool operator!=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
+template<typename... Args>
+[[nodiscard]] constexpr bool operator<(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
+    return lhs.it < rhs.it;
+}
+
+template<typename... Args>
+[[nodiscard]] constexpr bool operator>(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
+    return rhs < lhs;
+}
+
+template<typename... Args>
+[[nodiscard]] constexpr bool operator<=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
+    return !(lhs > rhs);
+}
+
+template<typename... Args>
+[[nodiscard]] constexpr bool operator>=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
+    return !(lhs < rhs);
+}
+
 } // namespace internal
 
 /**
@@ -67,57 +141,10 @@ private:
 /**
  * @brief Iterable range to use to iterate all types of meta objects.
  * @tparam Type Type of meta objects returned.
- * @tparam Node Type of meta nodes iterated.
+ * @tparam It Type of forward iterator.
  */
-template<typename Type, typename Node = typename Type::node_type>
-struct meta_range {
-    /*! @brief Node type. */
-    using node_type = Node;
-    /*! @brief Input iterator type. */
-    using iterator = internal::meta_range_iterator<Type, Node>;
-    /*! @brief Constant input iterator type. */
-    using const_iterator = iterator;
-
-    /*! @brief Default constructor. */
-    meta_range() ENTT_NOEXCEPT = default;
-
-    /**
-     * @brief Constructs a meta range from a given node.
-     * @param head The underlying node with which to construct the range.
-     */
-    meta_range(node_type *head)
-        : node{head} {}
-
-    /**
-     * @brief Returns an iterator to the beginning.
-     * @return An iterator to the first meta object of the range.
-     */
-    [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
-        return iterator{node};
-    }
-
-    /*! @copydoc cbegin */
-    [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-        return cbegin();
-    }
-
-    /**
-     * @brief Returns an iterator to the end.
-     * @return An iterator to the element following the last meta object of the
-     * range.
-     */
-    [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
-        return iterator{};
-    }
-
-    /*! @copydoc cend */
-    [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-        return cend();
-    }
-
-private:
-    node_type *node{nullptr};
-};
+template<typename Type, typename It>
+using meta_range = iterable_adaptor<internal::meta_range_iterator<Type, It>>;
 
 } // namespace entt
 

+ 57 - 18
Dependencies/include/entt/meta/resolve.hpp

@@ -1,9 +1,10 @@
 #ifndef ENTT_META_RESOLVE_HPP
 #define ENTT_META_RESOLVE_HPP
 
-#include <algorithm>
+#include <type_traits>
 #include "../core/type_info.hpp"
-#include "ctx.hpp"
+#include "../locator/locator.hpp"
+#include "context.hpp"
 #include "meta.hpp"
 #include "node.hpp"
 #include "range.hpp"
@@ -13,49 +14,87 @@ namespace entt {
 /**
  * @brief Returns the meta type associated with a given type.
  * @tparam Type Type to use to search for a meta type.
+ * @param ctx The context from which to search for meta types.
  * @return The meta type associated with the given type, if any.
  */
 template<typename Type>
-[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT {
-    return internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve();
+[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept {
+    auto &&context = internal::meta_context::from(ctx);
+    return {ctx, internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(context)};
+}
+
+/**
+ * @brief Returns the meta type associated with a given type.
+ * @tparam Type Type to use to search for a meta type.
+ * @return The meta type associated with the given type, if any.
+ */
+template<typename Type>
+[[nodiscard]] meta_type resolve() noexcept {
+    return resolve<Type>(locator<meta_ctx>::value_or());
 }
 
 /**
  * @brief Returns a range to use to visit all meta types.
+ * @param ctx The context from which to search for meta types.
  * @return An iterable range to use to visit all meta types.
  */
-[[nodiscard]] inline meta_range<meta_type> resolve() {
-    return *internal::meta_context::global();
+[[nodiscard]] inline meta_range<meta_type, typename decltype(internal::meta_context::value)::const_iterator> resolve(const meta_ctx &ctx) noexcept {
+    auto &&context = internal::meta_context::from(ctx);
+    return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}};
+}
+
+/**
+ * @brief Returns a range to use to visit all meta types.
+ * @return An iterable range to use to visit all meta types.
+ */
+[[nodiscard]] inline meta_range<meta_type, typename decltype(internal::meta_context::value)::const_iterator> resolve() noexcept {
+    return resolve(locator<meta_ctx>::value_or());
 }
 
 /**
  * @brief Returns the meta type associated with a given identifier, if any.
+ * @param ctx The context from which to search for meta types.
  * @param id Unique identifier.
  * @return The meta type associated with the given identifier, if any.
  */
-[[nodiscard]] inline meta_type resolve(const id_type id) ENTT_NOEXCEPT {
-    for(auto &&curr: resolve()) {
-        if(curr.id() == id) {
-            return curr;
+[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept {
+    for(auto &&curr: resolve(ctx)) {
+        if(curr.second.id() == id) {
+            return curr.second;
         }
     }
 
-    return {};
+    return meta_type{};
+}
+
+/**
+ * @brief Returns the meta type associated with a given identifier, if any.
+ * @param id Unique identifier.
+ * @return The meta type associated with the given identifier, if any.
+ */
+[[nodiscard]] inline meta_type resolve(const id_type id) noexcept {
+    return resolve(locator<meta_ctx>::value_or(), id);
 }
 
 /**
  * @brief Returns the meta type associated with a given type info object.
+ * @param ctx The context from which to search for meta types.
  * @param info The type info object of the requested type.
  * @return The meta type associated with the given type info object, if any.
  */
-[[nodiscard]] inline meta_type resolve(const type_info &info) ENTT_NOEXCEPT {
-    for(auto &&curr: resolve()) {
-        if(curr.info() == info) {
-            return curr;
-        }
-    }
+[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept {
+    auto &&context = internal::meta_context::from(ctx);
+    const auto *elem = internal::try_resolve(context, info);
+    return elem ? meta_type{ctx, *elem} : meta_type{};
+}
 
-    return {};
+/**
+ * @brief Returns the meta type associated with a given type info object.
+ * @param info The type info object of the requested type.
+ * @return The meta type associated with the given type info object, if any.
+ */
+[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept {
+    return resolve(locator<meta_ctx>::value_or(), info);
 }
 
 } // namespace entt

+ 2 - 2
Dependencies/include/entt/meta/template.hpp

@@ -6,7 +6,7 @@
 namespace entt {
 
 /*! @brief Utility class to disambiguate class templates. */
-template<template<typename...> typename>
+template<template<typename...> class>
 struct meta_class_template_tag {};
 
 /**
@@ -14,7 +14,7 @@ struct meta_class_template_tag {};
  * @tparam Clazz Type of class template.
  * @tparam Args Types of template arguments.
  */
-template<template<typename...> typename Clazz, typename... Args>
+template<template<typename...> class Clazz, typename... Args>
 struct meta_template_traits<Clazz<Args...>> {
     /*! @brief Wrapped class template. */
     using class_type = meta_class_template_tag<Clazz>;

+ 0 - 1
Dependencies/include/entt/meta/type_traits.hpp

@@ -30,7 +30,6 @@ struct meta_associative_container_traits;
 /**
  * @brief Provides the member constant `value` to true if a given type is a
  * pointer-like type from the point of view of the meta system, false otherwise.
- * @tparam Type Potentially pointer-like type.
  */
 template<typename>
 struct is_meta_pointer_like: std::false_type {};

+ 243 - 97
Dependencies/include/entt/meta/utility.hpp

@@ -5,14 +5,34 @@
 #include <functional>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
 #include "../core/type_traits.hpp"
+#include "../locator/locator.hpp"
 #include "meta.hpp"
 #include "node.hpp"
 #include "policy.hpp"
 
 namespace entt {
 
+/**
+ * @brief Meta function descriptor traits.
+ * @tparam Ret Function return type.
+ * @tparam Args Function arguments.
+ * @tparam Static Function staticness.
+ * @tparam Const Function constness.
+ */
+template<typename Ret, typename Args, bool Static, bool Const>
+struct meta_function_descriptor_traits {
+    /*! @brief Meta function return type. */
+    using return_type = Ret;
+    /*! @brief Meta function arguments. */
+    using args_type = Args;
+
+    /*! @brief True if the meta function is static, false otherwise. */
+    static constexpr bool is_static = Static;
+    /*! @brief True if the meta function is const, false otherwise. */
+    static constexpr bool is_const = Const;
+};
+
 /*! @brief Primary template isn't defined on purpose. */
 template<typename, typename>
 struct meta_function_descriptor;
@@ -25,17 +45,12 @@ struct meta_function_descriptor;
  * @tparam Args Function arguments.
  */
 template<typename Type, typename Ret, typename Class, typename... Args>
-struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const> {
-    /*! @brief Meta function return type. */
-    using return_type = Ret;
-    /*! @brief Meta function arguments. */
-    using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<const Class &, Args...>>;
-
-    /*! @brief True if the meta function is const, false otherwise. */
-    static constexpr auto is_const = true;
-    /*! @brief True if the meta function is static, false otherwise. */
-    static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
-};
+struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const>
+    : meta_function_descriptor_traits<
+          Ret,
+          std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<const Class &, Args...>>,
+          !std::is_base_of_v<Class, Type>,
+          true> {};
 
 /**
  * @brief Meta function descriptor.
@@ -45,17 +60,12 @@ struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const> {
  * @tparam Args Function arguments.
  */
 template<typename Type, typename Ret, typename Class, typename... Args>
-struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> {
-    /*! @brief Meta function return type. */
-    using return_type = Ret;
-    /*! @brief Meta function arguments. */
-    using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<Class &, Args...>>;
-
-    /*! @brief True if the meta function is const, false otherwise. */
-    static constexpr auto is_const = false;
-    /*! @brief True if the meta function is static, false otherwise. */
-    static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
-};
+struct meta_function_descriptor<Type, Ret (Class::*)(Args...)>
+    : meta_function_descriptor_traits<
+          Ret,
+          std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<Class &, Args...>>,
+          !std::is_base_of_v<Class, Type>,
+          false> {};
 
 /**
  * @brief Meta function descriptor.
@@ -64,17 +74,12 @@ struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> {
  * @tparam Ret Data member type.
  */
 template<typename Type, typename Ret, typename Class>
-struct meta_function_descriptor<Type, Ret Class::*> {
-    /*! @brief Meta data return type. */
-    using return_type = Ret &;
-    /*! @brief Meta data arguments. */
-    using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>;
-
-    /*! @brief True if the meta data is const, false otherwise. */
-    static constexpr auto is_const = false;
-    /*! @brief True if the meta data is static, false otherwise. */
-    static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
-};
+struct meta_function_descriptor<Type, Ret Class::*>
+    : meta_function_descriptor_traits<
+          Ret &,
+          std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>,
+          !std::is_base_of_v<Class, Type>,
+          false> {};
 
 /**
  * @brief Meta function descriptor.
@@ -84,17 +89,15 @@ struct meta_function_descriptor<Type, Ret Class::*> {
  * @tparam Args Other function arguments.
  */
 template<typename Type, typename Ret, typename MaybeType, typename... Args>
-struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> {
-    /*! @brief Meta function return type. */
-    using return_type = Ret;
-    /*! @brief Meta function arguments. */
-    using args_type = std::conditional_t<std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>, type_list<Args...>, type_list<MaybeType, Args...>>;
-
-    /*! @brief True if the meta function is const, false otherwise. */
-    static constexpr auto is_const = std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>;
-    /*! @brief True if the meta function is static, false otherwise. */
-    static constexpr auto is_static = !std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>;
-};
+struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)>
+    : meta_function_descriptor_traits<
+          Ret,
+          std::conditional_t<
+              std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>,
+              type_list<Args...>,
+              type_list<MaybeType, Args...>>,
+          !(std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>),
+          std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>)> {};
 
 /**
  * @brief Meta function descriptor.
@@ -102,17 +105,12 @@ struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> {
  * @tparam Ret Function return type.
  */
 template<typename Type, typename Ret>
-struct meta_function_descriptor<Type, Ret (*)()> {
-    /*! @brief Meta function return type. */
-    using return_type = Ret;
-    /*! @brief Meta function arguments. */
-    using args_type = type_list<>;
-
-    /*! @brief True if the meta function is const, false otherwise. */
-    static constexpr auto is_const = false;
-    /*! @brief True if the meta function is static, false otherwise. */
-    static constexpr auto is_static = true;
-};
+struct meta_function_descriptor<Type, Ret (*)()>
+    : meta_function_descriptor_traits<
+          Ret,
+          type_list<>,
+          true,
+          false> {};
 
 /**
  * @brief Meta function helper.
@@ -155,34 +153,65 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
 
 /**
  * @brief Wraps a value depending on the given policy.
+ *
+ * This function always returns a wrapped value in the requested context.<br/>
+ * Therefore, if the passed value is itself a wrapped object with a different
+ * context, it undergoes a rebinding to the requested context.
+ *
  * @tparam Policy Optional policy (no policy set by default).
  * @tparam Type Type of value to wrap.
+ * @param ctx The context from which to search for meta types.
  * @param value Value to wrap.
  * @return A meta any containing the returned value, if any.
  */
 template<typename Policy = as_is_t, typename Type>
-meta_any meta_dispatch([[maybe_unused]] Type &&value) {
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
     if constexpr(std::is_same_v<Policy, as_void_t>) {
-        return meta_any{std::in_place_type<void>};
+        return meta_any{ctx, std::in_place_type<void>};
     } else if constexpr(std::is_same_v<Policy, as_ref_t>) {
-        return meta_any{std::in_place_type<Type>, std::forward<Type>(value)};
+        return meta_any{ctx, std::in_place_type<Type>, value};
     } else if constexpr(std::is_same_v<Policy, as_cref_t>) {
         static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
-        return meta_any{std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
+        return meta_any{ctx, std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
     } else {
-        static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
-        return meta_any{std::forward<Type>(value)};
+        return meta_any{ctx, std::forward<Type>(value)};
     }
 }
 
+/**
+ * @brief Wraps a value depending on the given policy.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @tparam Type Type of value to wrap.
+ * @param value Value to wrap.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Policy = as_is_t, typename Type>
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
+    return meta_dispatch<Policy, Type>(locator<meta_ctx>::value_or(), std::forward<Type>(value));
+}
+
+/**
+ * @brief Returns the meta type of the i-th element of a list of arguments.
+ * @tparam Type Type list of the actual types of arguments.
+ * @param ctx The context from which to search for meta types.
+ * @param index The index of the element for which to return the meta type.
+ * @return The meta type of the i-th element of the list of arguments.
+ */
+template<typename Type>
+[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept {
+    auto &&context = internal::meta_context::from(ctx);
+    return {ctx, internal::meta_arg_node(context, Type{}, index)};
+}
+
 /**
  * @brief Returns the meta type of the i-th element of a list of arguments.
  * @tparam Type Type list of the actual types of arguments.
+ * @param index The index of the element for which to return the meta type.
  * @return The meta type of the i-th element of the list of arguments.
  */
 template<typename Type>
-[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT {
-    return internal::meta_arg_node(Type{}, index);
+[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept {
+    return meta_arg<Type>(locator<meta_ctx>::value_or(), index);
 }
 
 /**
@@ -230,41 +259,60 @@ template<typename Type, auto Data>
 
 /**
  * @brief Gets the value of a given variable.
+ *
+ * @warning
+ * The context provided is used only for the return type.<br/>
+ * It's up to the caller to bind the arguments to the right context(s).
+ *
  * @tparam Type Reflected type to which the variable is associated.
  * @tparam Data The actual variable to get.
  * @tparam Policy Optional policy (no policy set by default).
+ * @param ctx The context from which to search for meta types.
  * @param instance An opaque instance of the underlying type, if required.
  * @return A meta any containing the value of the underlying variable.
  */
 template<typename Type, auto Data, typename Policy = as_is_t>
-[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) {
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) {
     if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
         if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
             if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
                 if(auto *clazz = instance->try_cast<Type>(); clazz) {
-                    return meta_dispatch<Policy>(std::invoke(Data, *clazz));
+                    return meta_dispatch<Policy>(ctx, std::invoke(Data, *clazz));
                 }
             }
 
             if constexpr(std::is_invocable_v<decltype(Data), const Type &>) {
                 if(auto *fallback = instance->try_cast<const Type>(); fallback) {
-                    return meta_dispatch<Policy>(std::invoke(Data, *fallback));
+                    return meta_dispatch<Policy>(ctx, std::invoke(Data, *fallback));
                 }
             }
         }
 
-        return meta_any{};
+        return meta_any{meta_ctx_arg, ctx};
     } else if constexpr(std::is_pointer_v<decltype(Data)>) {
         if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
-            return meta_any{};
+            return meta_any{meta_ctx_arg, ctx};
         } else {
-            return meta_dispatch<Policy>(*Data);
+            return meta_dispatch<Policy>(ctx, *Data);
         }
     } else {
-        return meta_dispatch<Policy>(Data);
+        return meta_dispatch<Policy>(ctx, Data);
     }
 }
 
+/**
+ * @brief Gets the value of a given variable.
+ * @tparam Type Reflected type to which the variable is associated.
+ * @tparam Data The actual variable to get.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param instance An opaque instance of the underlying type, if required.
+ * @return A meta any containing the value of the underlying variable.
+ */
+template<typename Type, auto Data, typename Policy = as_is_t>
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(meta_handle instance) {
+    return meta_getter<Type, Data, Policy>(locator<meta_ctx>::value_or(), std::move(instance));
+}
+
 /**
  * @cond TURN_OFF_DOXYGEN
  * Internal details not to be documented.
@@ -272,44 +320,44 @@ template<typename Type, auto Data, typename Policy = as_is_t>
 
 namespace internal {
 
-template<typename Type, typename Policy, typename Candidate, typename... Args>
-[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) {
-    if constexpr(std::is_same_v<std::invoke_result_t<decltype(candidate), Args...>, void>) {
-        std::invoke(candidate, args...);
-        return meta_any{std::in_place_type<void>};
+template<typename Policy, typename Candidate, typename... Args>
+[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) {
+    if constexpr(std::is_void_v<decltype(std::invoke(std::forward<Candidate>(candidate), args...))>) {
+        std::invoke(std::forward<Candidate>(candidate), args...);
+        return meta_any{ctx, std::in_place_type<void>};
     } else {
-        return meta_dispatch<Policy>(std::invoke(candidate, args...));
+        return meta_dispatch<Policy>(ctx, std::invoke(std::forward<Candidate>(candidate), args...));
     }
 }
 
 template<typename Type, typename Policy, typename Candidate, std::size_t... Index>
-[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence<Index...>) {
+[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence<Index...>) {
     using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>;
 
     if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
         if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
-            return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+            return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
         }
     } else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
         if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
-            return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+            return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
         }
     } else {
         if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
-            return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+            return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
         }
     }
 
-    return meta_any{};
+    return meta_any{meta_ctx_arg, ctx};
 }
 
 template<typename Type, typename... Args, std::size_t... Index>
-[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence<Index...>) {
+[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence<Index...>) {
     if(((args + Index)->allow_cast<Args>() && ...)) {
-        return meta_any{std::in_place_type<Type>, (args + Index)->cast<Args>()...};
+        return meta_any{ctx, std::in_place_type<Type>, (args + Index)->cast<Args>()...};
     }
 
-    return meta_any{};
+    return meta_any{meta_ctx_arg, ctx};
 }
 
 } // namespace internal
@@ -319,6 +367,27 @@ template<typename Type, typename... Args, std::size_t... Index>
  * @endcond
  */
 
+/**
+ * @brief Tries to _invoke_ an object given a list of erased parameters.
+ *
+ * @warning
+ * The context provided is used only for the return type.<br/>
+ * It's up to the caller to bind the arguments to the right context(s).
+ *
+ * @tparam Type Reflected type to which the object to _invoke_ is associated.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param ctx The context from which to search for meta types.
+ * @tparam Candidate The type of the actual object to _invoke_.
+ * @param instance An opaque instance of the underlying type, if required.
+ * @param candidate The actual object to _invoke_.
+ * @param args Parameters to use to _invoke_ the object.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, typename Policy = as_is_t, typename Candidate>
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) {
+    return internal::meta_invoke<Type, Policy>(ctx, std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+}
+
 /**
  * @brief Tries to _invoke_ an object given a list of erased parameters.
  * @tparam Type Reflected type to which the object to _invoke_ is associated.
@@ -330,8 +399,28 @@ template<typename Type, typename... Args, std::size_t... Index>
  * @return A meta any containing the returned value, if any.
  */
 template<typename Type, typename Policy = as_is_t, typename Candidate>
-[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) {
-    return internal::meta_invoke<Type, Policy>(std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) {
+    return meta_invoke<Type, Policy>(locator<meta_ctx>::value_or(), std::move(instance), std::forward<Candidate>(candidate), args);
+}
+
+/**
+ * @brief Tries to invoke a function given a list of erased parameters.
+ *
+ * @warning
+ * The context provided is used only for the return type.<br/>
+ * It's up to the caller to bind the arguments to the right context(s).
+ *
+ * @tparam Type Reflected type to which the function is associated.
+ * @tparam Candidate The actual function to invoke.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param ctx The context from which to search for meta types.
+ * @param instance An opaque instance of the underlying type, if required.
+ * @param args Parameters to use to invoke the function.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, auto Candidate, typename Policy = as_is_t>
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) {
+    return internal::meta_invoke<Type, Policy>(ctx, std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
 }
 
 /**
@@ -344,8 +433,26 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
  * @return A meta any containing the returned value, if any.
  */
 template<typename Type, auto Candidate, typename Policy = as_is_t>
-[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) {
-    return internal::meta_invoke<Type, Policy>(std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, meta_any *const args) {
+    return meta_invoke<Type, Candidate, Policy>(locator<meta_ctx>::value_or(), std::move(instance), args);
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ *
+ * @warning
+ * The context provided is used only for the return type.<br/>
+ * It's up to the caller to bind the arguments to the right context(s).
+ *
+ * @tparam Type Actual type of the instance to construct.
+ * @tparam Args Types of arguments expected.
+ * @param ctx The context from which to search for meta types.
+ * @param args Parameters to use to construct the instance.
+ * @return A meta any containing the new instance, if any.
+ */
+template<typename Type, typename... Args>
+[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) {
+    return internal::meta_construct<Type, Args...>(ctx, args, std::index_sequence_for<Args...>{});
 }
 
 /**
@@ -357,38 +464,77 @@ template<typename Type, auto Candidate, typename Policy = as_is_t>
  */
 template<typename Type, typename... Args>
 [[nodiscard]] meta_any meta_construct(meta_any *const args) {
-    return internal::meta_construct<Type, Args...>(args, std::index_sequence_for<Args...>{});
+    return meta_construct<Type, Args...>(locator<meta_ctx>::value_or(), args);
 }
 
 /**
  * @brief Tries to construct an instance given a list of erased parameters.
+ *
+ * @warning
+ * The context provided is used only for the return type.<br/>
+ * It's up to the caller to bind the arguments to the right context(s).
+ *
  * @tparam Type Reflected type to which the object to _invoke_ is associated.
  * @tparam Policy Optional policy (no policy set by default).
  * @tparam Candidate The type of the actual object to _invoke_.
- * @param args Parameters to use to _invoke_ the object.
+ * @param ctx The context from which to search for meta types.
  * @param candidate The actual object to _invoke_.
+ * @param args Parameters to use to _invoke_ the object.
  * @return A meta any containing the returned value, if any.
  */
 template<typename Type, typename Policy = as_is_t, typename Candidate>
-[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) {
-    if constexpr(meta_function_helper_t<Type, Candidate>::is_static) {
-        return internal::meta_invoke<Type, Policy>({}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) {
+    if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_cv_t<std::remove_reference_t<Candidate>>>) {
+        return internal::meta_invoke<Type, Policy>(ctx, {}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
     } else {
-        return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+        return internal::meta_invoke<Type, Policy>(ctx, *args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
     }
 }
 
 /**
  * @brief Tries to construct an instance given a list of erased parameters.
+ * @tparam Type Reflected type to which the object to _invoke_ is associated.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @tparam Candidate The type of the actual object to _invoke_.
+ * @param candidate The actual object to _invoke_.
+ * @param args Parameters to use to _invoke_ the object.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, typename Policy = as_is_t, typename Candidate>
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) {
+    return meta_construct<Type, Policy>(locator<meta_ctx>::value_or(), std::forward<Candidate>(candidate), args);
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ *
+ * @warning
+ * The context provided is used only for the return type.<br/>
+ * It's up to the caller to bind the arguments to the right context(s).
+ *
  * @tparam Type Reflected type to which the function is associated.
  * @tparam Candidate The actual function to invoke.
  * @tparam Policy Optional policy (no policy set by default).
+ * @param ctx The context from which to search for meta types.
  * @param args Parameters to use to invoke the function.
  * @return A meta any containing the returned value, if any.
  */
 template<typename Type, auto Candidate, typename Policy = as_is_t>
-[[nodiscard]] meta_any meta_construct(meta_any *const args) {
-    return meta_construct<Type, Policy>(Candidate, args);
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) {
+    return meta_construct<Type, Policy>(ctx, Candidate, args);
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ * @tparam Type Reflected type to which the function is associated.
+ * @tparam Candidate The actual function to invoke.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param args Parameters to use to invoke the function.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, auto Candidate, typename Policy = as_is_t>
+[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(meta_any *const args) {
+    return meta_construct<Type, Candidate, Policy>(locator<meta_ctx>::value_or(), args);
 }
 
 } // namespace entt

+ 3 - 3
Dependencies/include/entt/poly/fwd.hpp

@@ -1,11 +1,11 @@
 #ifndef ENTT_POLY_FWD_HPP
 #define ENTT_POLY_FWD_HPP
 
-#include <type_traits>
+#include <cstddef>
 
 namespace entt {
 
-template<typename, std::size_t Len, std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+template<typename, std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
 class basic_poly;
 
 /**
@@ -13,7 +13,7 @@ class basic_poly;
  * @tparam Concept Concept descriptor.
  */
 template<typename Concept>
-using poly = basic_poly<Concept, sizeof(double[2])>;
+using poly = basic_poly<Concept>;
 
 } // namespace entt
 

+ 24 - 25
Dependencies/include/entt/poly/poly.hpp

@@ -6,7 +6,6 @@
 #include <tuple>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
 #include "../core/any.hpp"
 #include "../core/type_info.hpp"
 #include "../core/type_traits.hpp"
@@ -20,7 +19,7 @@ struct poly_inspector {
      * @brief Generic conversion operator (definition only).
      * @tparam Type Type to which conversion is requested.
      */
-    template<class Type>
+    template<typename Type>
     operator Type &&() const;
 
     /**
@@ -30,11 +29,11 @@ struct poly_inspector {
      * @param args The arguments to pass to the function.
      * @return A poly inspector convertible to any type.
      */
-    template<auto Member, typename... Args>
+    template<std::size_t Member, typename... Args>
     poly_inspector invoke(Args &&...args) const;
 
     /*! @copydoc invoke */
-    template<auto Member, typename... Args>
+    template<std::size_t Member, typename... Args>
     poly_inspector invoke(Args &&...args);
 };
 
@@ -64,11 +63,11 @@ class poly_vtable {
     static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...);
 
     template<auto... Candidate>
-    static auto make_vtable(value_list<Candidate...>)
+    static auto make_vtable(value_list<Candidate...>) noexcept
         -> decltype(std::make_tuple(vtable_entry(Candidate)...));
 
     template<typename... Func>
-    [[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) {
+    [[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) noexcept {
         if constexpr(sizeof...(Func) == 0u) {
             return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
         } else if constexpr((std::is_function_v<Func> && ...)) {
@@ -77,7 +76,7 @@ class poly_vtable {
     }
 
     template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
-    static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) {
+    static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept {
         if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
             entry = +[](Any &, Args... args) -> Ret {
                 return std::invoke(Candidate, std::forward<Args>(args)...);
@@ -90,7 +89,7 @@ class poly_vtable {
     }
 
     template<typename Type, auto... Index>
-    [[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) {
+    [[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) noexcept {
         vtable_type impl{};
         (fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...);
         return impl;
@@ -109,7 +108,7 @@ public:
      * @return A static virtual table for the given concept and type.
      */
     template<typename Type>
-    [[nodiscard]] static type instance() {
+    [[nodiscard]] static type instance() noexcept {
         static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form");
         static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
 
@@ -135,7 +134,7 @@ struct poly_base {
      * @param args The arguments to pass to the function.
      * @return The return value of the invoked function, if any.
      */
-    template<auto Member, typename... Args>
+    template<std::size_t Member, typename... Args>
     [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const {
         const auto &poly = static_cast<const Poly &>(self);
 
@@ -147,11 +146,12 @@ struct poly_base {
     }
 
     /*! @copydoc invoke */
-    template<auto Member, typename... Args>
+    template<std::size_t Member, typename... Args>
     [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) {
         auto &poly = static_cast<Poly &>(self);
 
         if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
+            static_assert(Member == 0u, "Unknown member");
             return poly.vtable(poly.storage, std::forward<Args>(args)...);
         } else {
             return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
@@ -168,7 +168,7 @@ struct poly_base {
  * @param args The arguments to pass to the function.
  * @return The return value of the invoked function, if any.
  */
-template<auto Member, typename Poly, typename... Args>
+template<std::size_t Member, typename Poly, typename... Args>
 decltype(auto) poly_call(Poly &&self, Args &&...args) {
     return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
 }
@@ -190,7 +190,6 @@ decltype(auto) poly_call(Poly &&self, Args &&...args) {
  */
 template<typename Concept, std::size_t Len, std::size_t Align>
 class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> {
-    /*! @brief A poly base is allowed to snoop into a poly object. */
     friend struct poly_base<basic_poly>;
 
 public:
@@ -200,7 +199,7 @@ public:
     using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
 
     /*! @brief Default constructor. */
-    basic_poly() ENTT_NOEXCEPT
+    basic_poly() noexcept
         : storage{},
           vtable{} {}
 
@@ -213,7 +212,7 @@ public:
     template<typename Type, typename... Args>
     explicit basic_poly(std::in_place_type_t<Type>, Args &&...args)
         : storage{std::in_place_type<Type>, std::forward<Args>(args)...},
-          vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_const_t<std::remove_reference_t<Type>>>()} {}
+          vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>()} {}
 
     /**
      * @brief Constructs a poly from a given value.
@@ -221,14 +220,14 @@ public:
      * @param value An instance of an object to use to initialize the poly.
      */
     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>>
-    basic_poly(Type &&value) ENTT_NOEXCEPT
+    basic_poly(Type &&value) noexcept
         : basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
 
     /**
      * @brief Returns the object type if any, `type_id<void>()` otherwise.
      * @return The object type if any, `type_id<void>()` otherwise.
      */
-    [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
+    [[nodiscard]] const type_info &type() const noexcept {
         return storage.type();
     }
 
@@ -236,12 +235,12 @@ public:
      * @brief Returns an opaque pointer to the contained instance.
      * @return An opaque pointer the contained instance, if any.
      */
-    [[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+    [[nodiscard]] const void *data() const noexcept {
         return storage.data();
     }
 
     /*! @copydoc data */
-    [[nodiscard]] void *data() ENTT_NOEXCEPT {
+    [[nodiscard]] void *data() noexcept {
         return storage.data();
     }
 
@@ -254,7 +253,7 @@ public:
     template<typename Type, typename... Args>
     void emplace(Args &&...args) {
         storage.template emplace<Type>(std::forward<Args>(args)...);
-        vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_const_t<std::remove_reference_t<Type>>>();
+        vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>();
     }
 
     /*! @brief Destroys contained object */
@@ -267,7 +266,7 @@ public:
      * @brief Returns false if a poly is empty, true otherwise.
      * @return False if the poly is empty, true otherwise.
      */
-    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+    [[nodiscard]] explicit operator bool() const noexcept {
         return static_cast<bool>(storage);
     }
 
@@ -275,12 +274,12 @@ public:
      * @brief Returns a pointer to the underlying concept.
      * @return A pointer to the underlying concept.
      */
-    [[nodiscard]] concept_type *operator->() ENTT_NOEXCEPT {
+    [[nodiscard]] concept_type *operator->() noexcept {
         return this;
     }
 
     /*! @copydoc operator-> */
-    [[nodiscard]] const concept_type *operator->() const ENTT_NOEXCEPT {
+    [[nodiscard]] const concept_type *operator->() const noexcept {
         return this;
     }
 
@@ -288,7 +287,7 @@ public:
      * @brief Aliasing constructor.
      * @return A poly that shares a reference to an unmanaged object.
      */
-    [[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT {
+    [[nodiscard]] basic_poly as_ref() noexcept {
         basic_poly ref{};
         ref.storage = storage.as_ref();
         ref.vtable = vtable;
@@ -296,7 +295,7 @@ public:
     }
 
     /*! @copydoc as_ref */
-    [[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT {
+    [[nodiscard]] basic_poly as_ref() const noexcept {
         basic_poly ref{};
         ref.storage = storage.as_ref();
         ref.vtable = vtable;

+ 20 - 0
Dependencies/include/entt/process/fwd.hpp

@@ -0,0 +1,20 @@
+#ifndef ENTT_PROCESS_FWD_HPP
+#define ENTT_PROCESS_FWD_HPP
+
+#include <cstdint>
+#include <memory>
+
+namespace entt {
+
+template<typename, typename>
+class process;
+
+template<typename = std::uint32_t, typename = std::allocator<void>>
+class basic_scheduler;
+
+/*! @brief Alias declaration for the most common use case. */
+using scheduler = basic_scheduler<>;
+
+} // namespace entt
+
+#endif

+ 14 - 14
Dependencies/include/entt/process/process.hpp

@@ -4,7 +4,7 @@
 #include <cstdint>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
+#include "fwd.hpp"
 
 namespace entt {
 
@@ -110,7 +110,7 @@ class process {
         static_cast<Target *>(this)->aborted();
     }
 
-    void next(...) const ENTT_NOEXCEPT {}
+    void next(...) const noexcept {}
 
 protected:
     /**
@@ -119,7 +119,7 @@ protected:
      * The function is idempotent and it does nothing if the process isn't
      * alive.
      */
-    void succeed() ENTT_NOEXCEPT {
+    void succeed() noexcept {
         if(alive()) {
             current = state::succeeded;
         }
@@ -131,7 +131,7 @@ protected:
      * The function is idempotent and it does nothing if the process isn't
      * alive.
      */
-    void fail() ENTT_NOEXCEPT {
+    void fail() noexcept {
         if(alive()) {
             current = state::failed;
         }
@@ -143,7 +143,7 @@ protected:
      * The function is idempotent and it does nothing if the process isn't
      * running.
      */
-    void pause() ENTT_NOEXCEPT {
+    void pause() noexcept {
         if(current == state::running) {
             current = state::paused;
         }
@@ -155,7 +155,7 @@ protected:
      * The function is idempotent and it does nothing if the process isn't
      * paused.
      */
-    void unpause() ENTT_NOEXCEPT {
+    void unpause() noexcept {
         if(current == state::paused) {
             current = state::running;
         }
@@ -166,7 +166,7 @@ public:
     using delta_type = Delta;
 
     /*! @brief Default destructor. */
-    virtual ~process() {
+    virtual ~process() noexcept {
         static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
     }
 
@@ -176,13 +176,13 @@ public:
      * The function is idempotent and it does nothing if the process isn't
      * alive.
      *
-     * @param immediately Requests an immediate operation.
+     * @param immediate Requests an immediate operation.
      */
-    void abort(const bool immediately = false) {
+    void abort(const bool immediate = false) {
         if(alive()) {
             current = state::aborted;
 
-            if(immediately) {
+            if(immediate) {
                 tick({});
             }
         }
@@ -192,7 +192,7 @@ public:
      * @brief Returns true if a process is either running or paused.
      * @return True if the process is still alive, false otherwise.
      */
-    [[nodiscard]] bool alive() const ENTT_NOEXCEPT {
+    [[nodiscard]] bool alive() const noexcept {
         return current == state::running || current == state::paused;
     }
 
@@ -200,7 +200,7 @@ public:
      * @brief Returns true if a process is already terminated.
      * @return True if the process is terminated, false otherwise.
      */
-    [[nodiscard]] bool finished() const ENTT_NOEXCEPT {
+    [[nodiscard]] bool finished() const noexcept {
         return current == state::finished;
     }
 
@@ -208,7 +208,7 @@ public:
      * @brief Returns true if a process is currently paused.
      * @return True if the process is paused, false otherwise.
      */
-    [[nodiscard]] bool paused() const ENTT_NOEXCEPT {
+    [[nodiscard]] bool paused() const noexcept {
         return current == state::paused;
     }
 
@@ -216,7 +216,7 @@ public:
      * @brief Returns true if a process terminated with errors.
      * @return True if the process terminated with errors, false otherwise.
      */
-    [[nodiscard]] bool rejected() const ENTT_NOEXCEPT {
+    [[nodiscard]] bool rejected() const noexcept {
         return current == state::rejected;
     }
 

+ 179 - 109
Dependencies/include/entt/process/scheduler.hpp

@@ -1,16 +1,64 @@
 #ifndef ENTT_PROCESS_SCHEDULER_HPP
 #define ENTT_PROCESS_SCHEDULER_HPP
 
-#include <algorithm>
+#include <cstddef>
 #include <memory>
 #include <type_traits>
 #include <utility>
 #include <vector>
 #include "../config/config.h"
+#include "../core/compressed_pair.hpp"
+#include "fwd.hpp"
 #include "process.hpp"
 
 namespace entt {
 
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Delta>
+struct basic_process_handler {
+    virtual ~basic_process_handler() = default;
+
+    virtual bool update(const Delta, void *) = 0;
+    virtual void abort(const bool) = 0;
+
+    // std::shared_ptr because of its type erased allocator which is useful here
+    std::shared_ptr<basic_process_handler> next;
+};
+
+template<typename Delta, typename Type>
+struct process_handler final: basic_process_handler<Delta> {
+    template<typename... Args>
+    process_handler(Args &&...args)
+        : process{std::forward<Args>(args)...} {}
+
+    bool update(const Delta delta, void *data) override {
+        if(process.tick(delta, data); process.rejected()) {
+            this->next.reset();
+        }
+
+        return (process.rejected() || process.finished());
+    }
+
+    void abort(const bool immediate) override {
+        process.abort(immediate);
+    }
+
+    Type process;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
 /**
  * @brief Cooperative scheduler for processes.
  *
@@ -36,100 +84,98 @@ namespace entt {
  * @sa process
  *
  * @tparam Delta Type to use to provide elapsed time.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Delta>
-class scheduler {
-    struct process_handler {
-        using instance_type = std::unique_ptr<void, void (*)(void *)>;
-        using update_fn_type = bool(process_handler &, Delta, void *);
-        using abort_fn_type = void(process_handler &, bool);
-        using next_type = std::unique_ptr<process_handler>;
-
-        instance_type instance;
-        update_fn_type *update;
-        abort_fn_type *abort;
-        next_type next;
-    };
-
-    struct continuation {
-        continuation(process_handler *ref)
-            : handler{ref} {}
-
-        template<typename Proc, typename... Args>
-        continuation then(Args &&...args) {
-            static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
-            auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
-            handler->next.reset(new process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
-            handler = handler->next.get();
-            return *this;
-        }
+template<typename Delta, typename Allocator>
+class basic_scheduler {
+    template<typename Type>
+    using handler_type = internal::process_handler<Delta, Type>;
 
-        template<typename Func>
-        continuation then(Func &&func) {
-            return then<process_adaptor<std::decay_t<Func>, Delta>>(std::forward<Func>(func));
-        }
+    // std::shared_ptr because of its type erased allocator which is useful here
+    using process_type = std::shared_ptr<internal::basic_process_handler<Delta>>;
 
-    private:
-        process_handler *handler;
-    };
-
-    template<typename Proc>
-    [[nodiscard]] static bool update(process_handler &handler, const Delta delta, void *data) {
-        auto *process = static_cast<Proc *>(handler.instance.get());
-        process->tick(delta, data);
-
-        if(process->rejected()) {
-            return true;
-        } else if(process->finished()) {
-            if(handler.next) {
-                handler = std::move(*handler.next);
-                // forces the process to exit the uninitialized state
-                return handler.update(handler, {}, nullptr);
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    template<typename Proc>
-    static void abort(process_handler &handler, const bool immediately) {
-        static_cast<Proc *>(handler.instance.get())->abort(immediately);
-    }
-
-    template<typename Proc>
-    static void deleter(void *proc) {
-        delete static_cast<Proc *>(proc);
-    }
+    using alloc_traits = std::allocator_traits<Allocator>;
+    using container_allocator = typename alloc_traits::template rebind_alloc<process_type>;
+    using container_type = std::vector<process_type, container_allocator>;
 
 public:
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
+    /*! @brief Unsigned integer type. */
+    using delta_type = Delta;
 
     /*! @brief Default constructor. */
-    scheduler() = default;
+    basic_scheduler()
+        : basic_scheduler{allocator_type{}} {}
 
-    /*! @brief Default move constructor. */
-    scheduler(scheduler &&) = default;
+    /**
+     * @brief Constructs a scheduler with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit basic_scheduler(const allocator_type &allocator)
+        : handlers{allocator, allocator} {}
 
-    /*! @brief Default move assignment operator. @return This scheduler. */
-    scheduler &operator=(scheduler &&) = default;
+    /**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+    basic_scheduler(basic_scheduler &&other) noexcept
+        : handlers{std::move(other.handlers)} {}
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) noexcept
+        : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {
+        ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
+    }
+
+    /**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This scheduler.
+     */
+    basic_scheduler &operator=(basic_scheduler &&other) noexcept {
+        ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
+        handlers = std::move(other.handlers);
+        return *this;
+    }
+
+    /**
+     * @brief Exchanges the contents with those of a given scheduler.
+     * @param other Scheduler to exchange the content with.
+     */
+    void swap(basic_scheduler &other) {
+        using std::swap;
+        swap(handlers, other.handlers);
+    }
+
+    /**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return handlers.second();
+    }
 
     /**
      * @brief Number of processes currently scheduled.
      * @return Number of processes currently scheduled.
      */
-    [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
-        return handlers.size();
+    [[nodiscard]] size_type size() const noexcept {
+        return handlers.first().size();
     }
 
     /**
      * @brief Returns true if at least a process is currently scheduled.
      * @return True if there are scheduled processes, false otherwise.
      */
-    [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
-        return handlers.empty();
+    [[nodiscard]] bool empty() const noexcept {
+        return handlers.first().empty();
     }
 
     /**
@@ -139,15 +185,15 @@ public:
      * and never executed again.
      */
     void clear() {
-        handlers.clear();
+        handlers.first().clear();
     }
 
     /**
      * @brief Schedules a process for the next tick.
      *
-     * Returned value is an opaque object that can be used to attach a child to
-     * the given process. The child is automatically scheduled when the process
-     * terminates and only if the process returns with success.
+     * Returned value can be used to attach a continuation for the last process.
+     * The continutation is scheduled automatically when the process terminates
+     * and only if the process returns with success.
      *
      * Example of use (pseudocode):
      *
@@ -165,16 +211,15 @@ public:
      * @tparam Proc Type of process to schedule.
      * @tparam Args Types of arguments to use to initialize the process.
      * @param args Parameters to use to initialize the process.
-     * @return An opaque object to use to concatenate processes.
+     * @return This process scheduler.
      */
     template<typename Proc, typename... Args>
-    auto attach(Args &&...args) {
+    basic_scheduler &attach(Args &&...args) {
         static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
-        auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
-        process_handler handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr};
+        auto &ref = handlers.first().emplace_back(std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...));
         // forces the process to exit the uninitialized state
-        handler.update(handler, {}, nullptr);
-        return continuation{&handlers.emplace_back(std::move(handler))};
+        ref->update({}, nullptr);
+        return *this;
     }
 
     /**
@@ -203,9 +248,9 @@ public:
      * void();
      * @endcode
      *
-     * Returned value is an opaque object that can be used to attach a child to
-     * the given process. The child is automatically scheduled when the process
-     * terminates and only if the process returns with success.
+     * Returned value can be used to attach a continuation for the last process.
+     * The continutation is scheduled automatically when the process terminates
+     * and only if the process returns with success.
      *
      * Example of use (pseudocode):
      *
@@ -226,14 +271,43 @@ public:
      *
      * @tparam Func Type of process to schedule.
      * @param func Either a lambda or a functor to use as a process.
-     * @return An opaque object to use to concatenate processes.
+     * @return This process scheduler.
      */
     template<typename Func>
-    auto attach(Func &&func) {
+    basic_scheduler &attach(Func &&func) {
         using Proc = process_adaptor<std::decay_t<Func>, Delta>;
         return attach<Proc>(std::forward<Func>(func));
     }
 
+    /**
+     * @brief Sets a process as a continuation of the last scheduled process.
+     * @tparam Proc Type of process to use as a continuation.
+     * @tparam Args Types of arguments to use to initialize the process.
+     * @param args Parameters to use to initialize the process.
+     * @return This process scheduler.
+     */
+    template<typename Proc, typename... Args>
+    basic_scheduler &then(Args &&...args) {
+        static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
+        ENTT_ASSERT(!handlers.first().empty(), "Process not available");
+        auto *curr = handlers.first().back().get();
+        for(; curr->next; curr = curr->next.get()) {}
+        curr->next = std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...);
+        return *this;
+    }
+
+    /**
+     * @brief Sets a process as a continuation of the last scheduled process.
+     * @tparam Func Type of process to use as a continuation.
+     * @param func Either a lambda or a functor to use as a process.
+     * @return This process scheduler.
+     */
+    template<typename Func>
+    basic_scheduler &then(Func &&func) {
+        using Proc = process_adaptor<std::decay_t<Func>, Delta>;
+        return then<Proc>(std::forward<Func>(func));
+    }
+
     /**
      * @brief Updates all scheduled processes.
      *
@@ -245,18 +319,20 @@ public:
      * @param delta Elapsed time.
      * @param data Optional data.
      */
-    void update(const Delta delta, void *data = nullptr) {
-        auto sz = handlers.size();
-
-        for(auto pos = handlers.size(); pos; --pos) {
-            auto &handler = handlers[pos - 1];
-
-            if(const auto dead = handler.update(handler, delta, data); dead) {
-                std::swap(handler, handlers[--sz]);
+    void update(const delta_type delta, void *data = nullptr) {
+        for(auto next = handlers.first().size(); next; --next) {
+            if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) {
+                // updating might spawn/reallocate, cannot hold refs until here
+                if(auto &curr = handlers.first()[pos]; curr->next) {
+                    curr = std::move(curr->next);
+                    // forces the process to exit the uninitialized state
+                    curr->update({}, nullptr);
+                } else {
+                    curr = std::move(handlers.first().back());
+                    handlers.first().pop_back();
+                }
             }
         }
-
-        handlers.erase(handlers.begin() + sz, handlers.end());
     }
 
     /**
@@ -267,22 +343,16 @@ public:
      * Once a process is fully aborted and thus finished, it's discarded along
      * with its child, if any.
      *
-     * @param immediately Requests an immediate operation.
+     * @param immediate Requests an immediate operation.
      */
-    void abort(const bool immediately = false) {
-        decltype(handlers) exec;
-        exec.swap(handlers);
-
-        for(auto &&handler: exec) {
-            handler.abort(handler, immediately);
+    void abort(const bool immediate = false) {
+        for(auto &&curr: handlers.first()) {
+            curr->abort(immediate);
         }
-
-        std::move(handlers.begin(), handlers.end(), std::back_inserter(exec));
-        handlers.swap(exec);
     }
 
 private:
-    std::vector<process_handler> handlers{};
+    compressed_pair<container_type, allocator_type> handlers;
 };
 
 } // namespace entt

+ 319 - 181
Dependencies/include/entt/resource/cache.hpp

@@ -1,178 +1,362 @@
-#ifndef ENTT_RESOURCE_CACHE_HPP
-#define ENTT_RESOURCE_CACHE_HPP
+#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP
+#define ENTT_RESOURCE_RESOURCE_CACHE_HPP
 
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <tuple>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
-#include "../container/dense_hash_map.hpp"
+#include "../container/dense_map.hpp"
+#include "../core/compressed_pair.hpp"
 #include "../core/fwd.hpp"
+#include "../core/iterator.hpp"
 #include "../core/utility.hpp"
 #include "fwd.hpp"
-#include "handle.hpp"
 #include "loader.hpp"
+#include "resource.hpp"
 
 namespace entt {
 
 /**
- * @brief Simple cache for resources of a given type.
- *
- * Minimal implementation of a cache for resources of a given type. It doesn't
- * offer much functionalities but it's suitable for small or medium sized
- * applications and can be freely inherited to add targeted functionalities for
- * large sized applications.
- *
- * @tparam Resource Type of resources managed by a cache.
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
  */
-template<typename Resource>
+
+namespace internal {
+
+template<typename Type, typename It>
+class resource_cache_iterator final {
+    template<typename, typename>
+    friend class resource_cache_iterator;
+
+public:
+    using value_type = std::pair<id_type, resource<Type>>;
+    using pointer = input_iterator_pointer<value_type>;
+    using reference = value_type;
+    using difference_type = std::ptrdiff_t;
+    using iterator_category = std::input_iterator_tag;
+    using iterator_concept = std::random_access_iterator_tag;
+
+    constexpr resource_cache_iterator() noexcept = default;
+
+    constexpr resource_cache_iterator(const It iter) noexcept
+        : it{iter} {}
+
+    template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+    constexpr resource_cache_iterator(const resource_cache_iterator<std::remove_const_t<Type>, Other> &other) noexcept
+        : it{other.it} {}
+
+    constexpr resource_cache_iterator &operator++() noexcept {
+        return ++it, *this;
+    }
+
+    constexpr resource_cache_iterator operator++(int) noexcept {
+        resource_cache_iterator orig = *this;
+        return ++(*this), orig;
+    }
+
+    constexpr resource_cache_iterator &operator--() noexcept {
+        return --it, *this;
+    }
+
+    constexpr resource_cache_iterator operator--(int) noexcept {
+        resource_cache_iterator orig = *this;
+        return operator--(), orig;
+    }
+
+    constexpr resource_cache_iterator &operator+=(const difference_type value) noexcept {
+        it += value;
+        return *this;
+    }
+
+    constexpr resource_cache_iterator operator+(const difference_type value) const noexcept {
+        resource_cache_iterator copy = *this;
+        return (copy += value);
+    }
+
+    constexpr resource_cache_iterator &operator-=(const difference_type value) noexcept {
+        return (*this += -value);
+    }
+
+    constexpr resource_cache_iterator operator-(const difference_type value) const noexcept {
+        return (*this + -value);
+    }
+
+    [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
+        return {it[value].first, resource<Type>{it[value].second}};
+    }
+
+    [[nodiscard]] constexpr reference operator*() const noexcept {
+        return (*this)[0];
+    }
+
+    [[nodiscard]] constexpr pointer operator->() const noexcept {
+        return operator*();
+    }
+
+    template<typename... Lhs, typename... Rhs>
+    friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
+
+    template<typename... Lhs, typename... Rhs>
+    friend constexpr bool operator==(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
+
+    template<typename... Lhs, typename... Rhs>
+    friend constexpr bool operator<(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
+
+private:
+    It it;
+};
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
+    return lhs.it - rhs.it;
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
+    return lhs.it == rhs.it;
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
+    return lhs.it < rhs.it;
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
+    return rhs < lhs;
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
+    return !(lhs > rhs);
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
+    return !(lhs < rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Basic cache for resources of any type.
+ * @tparam Type Type of resources managed by a cache.
+ * @tparam Loader Type of loader used to create the resources.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Loader, typename Allocator>
 class resource_cache {
-    static_assert(std::is_same_v<Resource, std::remove_const_t<std::remove_reference_t<Resource>>>, "Invalid resource type");
+    using alloc_traits = std::allocator_traits<Allocator>;
+    static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+    using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
+    using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>;
 
 public:
+    /*! @brief Resource type. */
+    using value_type = Type;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
-    /*! @brief Type of resources managed by a cache. */
-    using resource_type = Resource;
+    /*! @brief Loader type. */
+    using loader_type = Loader;
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Input iterator type. */
+    using iterator = internal::resource_cache_iterator<Type, typename container_type::iterator>;
+    /*! @brief Constant input iterator type. */
+    using const_iterator = internal::resource_cache_iterator<const Type, typename container_type::const_iterator>;
 
     /*! @brief Default constructor. */
-    resource_cache() = default;
+    resource_cache()
+        : resource_cache{loader_type{}} {}
+
+    /**
+     * @brief Constructs an empty cache with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit resource_cache(const allocator_type &allocator)
+        : resource_cache{loader_type{}, allocator} {}
+
+    /**
+     * @brief Constructs an empty cache with a given allocator and loader.
+     * @param callable The loader to use.
+     * @param allocator The allocator to use.
+     */
+    explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{})
+        : pool{container_type{allocator}, callable} {}
+
+    /*! @brief Default copy constructor. */
+    resource_cache(const resource_cache &) = default;
+
+    /**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+    resource_cache(const resource_cache &other, const allocator_type &allocator)
+        : pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {}
 
     /*! @brief Default move constructor. */
     resource_cache(resource_cache &&) = default;
 
-    /*! @brief Default move assignment operator. @return This cache. */
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    resource_cache(resource_cache &&other, const allocator_type &allocator)
+        : pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {}
+
+    /**
+     * @brief Default copy assignment operator.
+     * @return This cache.
+     */
+    resource_cache &operator=(const resource_cache &) = default;
+
+    /**
+     * @brief Default move assignment operator.
+     * @return This cache.
+     */
     resource_cache &operator=(resource_cache &&) = default;
 
     /**
-     * @brief Number of resources managed by a cache.
-     * @return Number of resources currently stored.
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
      */
-    [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
-        return resources.size();
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return pool.first().get_allocator();
+    }
+
+    /**
+     * @brief Returns an iterator to the beginning.
+     *
+     * If the cache is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal cache.
+     */
+    [[nodiscard]] const_iterator cbegin() const noexcept {
+        return pool.first().begin();
+    }
+
+    /*! @copydoc cbegin */
+    [[nodiscard]] const_iterator begin() const noexcept {
+        return cbegin();
+    }
+
+    /*! @copydoc begin */
+    [[nodiscard]] iterator begin() noexcept {
+        return pool.first().begin();
+    }
+
+    /**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last instance of the
+     * internal cache.
+     */
+    [[nodiscard]] const_iterator cend() const noexcept {
+        return pool.first().end();
+    }
+
+    /*! @copydoc cend */
+    [[nodiscard]] const_iterator end() const noexcept {
+        return cend();
+    }
+
+    /*! @copydoc end */
+    [[nodiscard]] iterator end() noexcept {
+        return pool.first().end();
     }
 
     /**
      * @brief Returns true if a cache contains no resources, false otherwise.
      * @return True if the cache contains no resources, false otherwise.
      */
-    [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
-        return resources.empty();
+    [[nodiscard]] bool empty() const noexcept {
+        return pool.first().empty();
     }
 
     /**
-     * @brief Clears a cache and discards all its resources.
-     *
-     * Handles are not invalidated and the memory used by a resource isn't
-     * freed as long as at least a handle keeps the resource itself alive.
+     * @brief Number of resources managed by a cache.
+     * @return Number of resources currently stored.
      */
-    void clear() ENTT_NOEXCEPT {
-        resources.clear();
+    [[nodiscard]] size_type size() const noexcept {
+        return pool.first().size();
+    }
+
+    /*! @brief Clears a cache. */
+    void clear() noexcept {
+        pool.first().clear();
     }
 
     /**
-     * @brief Loads the resource that corresponds to a given identifier.
+     * @brief Loads a resource, if its identifier does not exist.
      *
-     * In case an identifier isn't already present in the cache, it loads its
-     * resource and stores it aside for future uses. Arguments are forwarded
-     * directly to the loader in order to construct properly the requested
-     * resource.
-     *
-     * @note
-     * If the identifier is already present in the cache, this function does
-     * nothing and the arguments are simply discarded.
+     * Arguments are forwarded directly to the loader and _consumed_ only if the
+     * resource doesn't already exist.
      *
      * @warning
-     * If the resource cannot be loaded correctly, the returned handle will be
+     * If the resource isn't loaded correctly, the returned handle could be
      * invalid and any use of it will result in undefined behavior.
      *
-     * @tparam Loader Type of loader to use to load the resource if required.
      * @tparam Args Types of arguments to use to load the resource if required.
      * @param id Unique resource identifier.
      * @param args Arguments to use to load the resource if required.
-     * @return A handle for the given resource.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
      */
-    template<typename Loader, typename... Args>
-    resource_handle<resource_type> load(const id_type id, Args &&...args) {
-        if(auto it = resources.find(id); it == resources.cend()) {
-            if(auto handle = temp<Loader>(std::forward<Args>(args)...); handle) {
-                return (resources[id] = std::move(handle));
-            }
-        } else {
-            return it->second;
+    template<typename... Args>
+    std::pair<iterator, bool> load(const id_type id, Args &&...args) {
+        if(auto it = pool.first().find(id); it != pool.first().end()) {
+            return {it, false};
         }
 
-        return {};
+        return pool.first().emplace(id, pool.second()(std::forward<Args>(args)...));
     }
 
     /**
-     * @brief Reloads a resource or loads it for the first time if not present.
-     *
-     * Equivalent to the following snippet (pseudocode):
-     *
-     * @code{.cpp}
-     * cache.discard(id);
-     * cache.load(id, args...);
-     * @endcode
-     *
-     * Arguments are forwarded directly to the loader in order to construct
-     * properly the requested resource.
-     *
-     * @warning
-     * If the resource cannot be loaded correctly, the returned handle will be
-     * invalid and any use of it will result in undefined behavior.
-     *
-     * @tparam Loader Type of loader to use to load the resource.
-     * @tparam Args Types of arguments to use to load the resource.
-     * @param id Unique resource identifier.
-     * @param args Arguments to use to load the resource.
-     * @return A handle for the given resource.
-     */
-    template<typename Loader, typename... Args>
-    resource_handle<resource_type> reload(const id_type id, Args &&...args) {
-        return (discard(id), load<Loader>(id, std::forward<Args>(args)...));
-    }
-
-    /**
-     * @brief Creates a temporary handle for a resource.
-     *
-     * Arguments are forwarded directly to the loader in order to construct
-     * properly the requested resource. The handle isn't stored aside and the
-     * cache isn't in charge of the lifetime of the resource itself.
-     *
-     * @tparam Loader Type of loader to use to load the resource.
-     * @tparam Args Types of arguments to use to load the resource.
-     * @param args Arguments to use to load the resource.
-     * @return A handle for the given resource.
+     * @brief Force loads a resource, if its identifier does not exist.
+     * @copydetails load
      */
-    template<typename Loader, typename... Args>
-    [[nodiscard]] resource_handle<resource_type> temp(Args &&...args) const {
-        return Loader{}.get(std::forward<Args>(args)...);
+    template<typename... Args>
+    std::pair<iterator, bool> force_load(const id_type id, Args &&...args) {
+        return {pool.first().insert_or_assign(id, pool.second()(std::forward<Args>(args)...)).first, true};
     }
 
     /**
-     * @brief Creates a handle for a given resource identifier.
-     *
-     * A resource handle can be in a either valid or invalid state. In other
-     * terms, a resource handle is properly initialized with a resource if the
-     * cache contains the resource itself. Otherwise the returned handle is
-     * uninitialized and accessing it results in undefined behavior.
+     * @brief Returns a handle for a given resource identifier.
      *
-     * @sa resource_handle
+     * @warning
+     * There is no guarantee that the returned handle is valid.<br/>
+     * If it is not, any use will result in indefinite behavior.
      *
      * @param id Unique resource identifier.
      * @return A handle for the given resource.
      */
-    [[nodiscard]] resource_handle<const resource_type> handle(const id_type id) const {
-        if(auto it = resources.find(id); it != resources.cend()) {
-            return it->second;
+    [[nodiscard]] resource<const value_type> operator[](const id_type id) const {
+        if(auto it = pool.first().find(id); it != pool.first().cend()) {
+            return resource<const value_type>{it->second};
         }
 
         return {};
     }
 
-    /*! @copydoc handle */
-    [[nodiscard]] resource_handle<resource_type> handle(const id_type id) {
-        if(auto it = resources.find(id); it != resources.end()) {
-            return it->second;
+    /*! @copydoc operator[] */
+    [[nodiscard]] resource<value_type> operator[](const id_type id) {
+        if(auto it = pool.first().find(id); it != pool.first().end()) {
+            return resource<value_type>{it->second};
         }
 
         return {};
@@ -184,95 +368,49 @@ public:
      * @return True if the cache contains the resource, false otherwise.
      */
     [[nodiscard]] bool contains(const id_type id) const {
-        return (resources.find(id) != resources.cend());
+        return pool.first().contains(id);
     }
 
     /**
-     * @brief Discards the resource that corresponds to a given identifier.
-     *
-     * Handles are not invalidated and the memory used by the resource isn't
-     * freed as long as at least a handle keeps the resource itself alive.
-     *
-     * @param id Unique resource identifier.
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
      */
-    void discard(const id_type id) {
-        if(auto it = resources.find(id); it != resources.end()) {
-            resources.erase(it);
-        }
+    iterator erase(const_iterator pos) {
+        const auto it = pool.first().begin();
+        return pool.first().erase(it + (pos - const_iterator{it}));
     }
 
     /**
-     * @brief Iterates all resources.
-     *
-     * The function object is invoked for each element. It is provided with
-     * either the resource identifier, the resource handle or both of them.<br/>
-     * The signature of the function must be equivalent to one of the following
-     * forms:
-     *
-     * @code{.cpp}
-     * void(const entt::id_type);
-     * void(entt::resource_handle<const resource_type>);
-     * void(const entt::id_type, entt::resource_handle<const resource_type>);
-     * @endcode
-     *
-     * @tparam Func Type of the function object to invoke.
-     * @param func A valid function object.
+     * @brief Removes the given elements from a cache.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
      */
-    template<typename Func>
-    void each(Func func) const {
-        auto begin = resources.begin();
-        auto end = resources.end();
-
-        while(begin != end) {
-            auto curr = begin++;
-
-            if constexpr(std::is_invocable_v<Func, id_type>) {
-                func(curr->first);
-            } else if constexpr(std::is_invocable_v<Func, resource_handle<const resource_type>>) {
-                func(resource_handle<const resource_type>{curr->second});
-            } else {
-                func(curr->first, resource_handle<const resource_type>{curr->second});
-            }
-        }
+    iterator erase(const_iterator first, const_iterator last) {
+        const auto it = pool.first().begin();
+        return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it}));
     }
 
     /**
-     * @copybrief each
-     *
-     * The function object is invoked for each element. It is provided with
-     * either the resource identifier, the resource handle or both of them.<br/>
-     * The signature of the function must be equivalent to one of the following
-     * forms:
-     *
-     * @code{.cpp}
-     * void(const entt::id_type);
-     * void(entt::resource_handle<resource_type>);
-     * void(const entt::id_type, entt::resource_handle<resource_type>);
-     * @endcode
-     *
-     * @tparam Func Type of the function object to invoke.
-     * @param func A valid function object.
+     * @brief Removes the given elements from a cache.
+     * @param id Unique resource identifier.
+     * @return Number of resources erased (either 0 or 1).
      */
-    template<typename Func>
-    void each(Func func) {
-        auto begin = resources.begin();
-        auto end = resources.end();
-
-        while(begin != end) {
-            auto curr = begin++;
-
-            if constexpr(std::is_invocable_v<Func, id_type>) {
-                func(curr->first);
-            } else if constexpr(std::is_invocable_v<Func, resource_handle<resource_type>>) {
-                func(curr->second);
-            } else {
-                func(curr->first, curr->second);
-            }
-        }
+    size_type erase(const id_type id) {
+        return pool.first().erase(id);
+    }
+
+    /**
+     * @brief Returns the loader used to create resources.
+     * @return The loader used to create resources.
+     */
+    [[nodiscard]] loader_type loader() const {
+        return pool.second();
     }
 
 private:
-    dense_hash_map<id_type, resource_handle<resource_type>, identity> resources;
+    compressed_pair<container_type, loader_type> pool;
 };
 
 } // namespace entt

+ 6 - 4
Dependencies/include/entt/resource/fwd.hpp

@@ -1,16 +1,18 @@
 #ifndef ENTT_RESOURCE_FWD_HPP
 #define ENTT_RESOURCE_FWD_HPP
 
+#include <memory>
+
 namespace entt {
 
 template<typename>
+struct resource_loader;
+
+template<typename Type, typename = resource_loader<Type>, typename = std::allocator<Type>>
 class resource_cache;
 
 template<typename>
-class resource_handle;
-
-template<typename, typename>
-class resource_loader;
+class resource;
 
 } // namespace entt
 

+ 0 - 183
Dependencies/include/entt/resource/handle.hpp

@@ -1,183 +0,0 @@
-#ifndef ENTT_RESOURCE_HANDLE_HPP
-#define ENTT_RESOURCE_HANDLE_HPP
-
-#include <memory>
-#include <type_traits>
-#include <utility>
-#include "../config/config.h"
-#include "fwd.hpp"
-
-namespace entt {
-
-/**
- * @brief Shared resource handle.
- *
- * A shared resource handle is a small class that wraps a resource and keeps it
- * alive even if it's deleted from the cache. It can be either copied or
- * moved. A handle shares a reference to the same resource with all the other
- * handles constructed for the same identifier.<br/>
- * As a rule of thumb, resources should never be copied nor moved. Handles are
- * the way to go to keep references to them.
- *
- * @tparam Resource Type of resource managed by a handle.
- */
-template<typename Resource>
-class resource_handle {
-    /*! @brief Resource handles are friends with each other. */
-    template<typename>
-    friend class resource_handle;
-
-public:
-    /*! @brief Unsigned integer type. */
-    using size_type = long;
-    /*! @brief Type of resources managed by a cache. */
-    using resource_type = Resource;
-
-    /*! @brief Default constructor. */
-    resource_handle() ENTT_NOEXCEPT = default;
-
-    /**
-     * @brief Creates a handle from a shared pointer, namely a resource.
-     * @param res A pointer to a properly initialized resource.
-     */
-    resource_handle(std::shared_ptr<resource_type> res) ENTT_NOEXCEPT
-        : resource{std::move(res)} {}
-
-    /**
-     * @brief Copy constructor.
-     * @param other The instance to copy from.
-     */
-    resource_handle(const resource_handle &other) ENTT_NOEXCEPT = default;
-
-    /**
-     * @brief Move constructor.
-     * @param other The instance to move from.
-     */
-    resource_handle(resource_handle &&other) ENTT_NOEXCEPT = default;
-
-    /**
-     * @brief Aliasing constructor.
-     * @tparam Other Type of resource managed by the received handle.
-     * @param other The handle with which to share ownership information.
-     * @param res Unrelated and unmanaged resources.
-     */
-    template<typename Other>
-    resource_handle(const resource_handle<Other> &other, resource_type &res) noexcept
-        : resource{other.resource, std::addressof(res)} {}
-
-    /**
-     * @brief Copy constructs a handle which shares ownership of the resource.
-     * @tparam Other Type of resource managed by the received handle.
-     * @param other The handle to copy from.
-     */
-    template<typename Other, typename = std::enable_if_t<!std::is_same_v<resource_type, Other> && std::is_base_of_v<resource_type, Other>>>
-    resource_handle(const resource_handle<Other> &other) ENTT_NOEXCEPT
-        : resource{other.resource} {}
-
-    /**
-     * @brief Move constructs a handle which takes ownership of the resource.
-     * @tparam Other Type of resource managed by the received handle.
-     * @param other The handle to move from.
-     */
-    template<typename Other, typename = std::enable_if_t<!std::is_same_v<resource_type, Other> && std::is_base_of_v<resource_type, Other>>>
-    resource_handle(resource_handle<Other> &&other) ENTT_NOEXCEPT
-        : resource{std::move(other.resource)} {}
-
-    /**
-     * @brief Copy assignment operator.
-     * @param other The instance to copy from.
-     * @return This resource handle.
-     */
-    resource_handle &operator=(const resource_handle &other) ENTT_NOEXCEPT = default;
-
-    /**
-     * @brief Move assignment operator.
-     * @param other The instance to move from.
-     * @return This resource handle.
-     */
-    resource_handle &operator=(resource_handle &&other) ENTT_NOEXCEPT = default;
-
-    /**
-     * @brief Copy assignment operator from foreign handle.
-     * @tparam Other Type of resource managed by the received handle.
-     * @param other The handle to copy from.
-     * @return This resource handle.
-     */
-    template<typename Other>
-    std::enable_if_t<!std::is_same_v<resource_type, Other> && std::is_base_of_v<resource_type, Other>, resource_handle &>
-    operator=(const resource_handle<Other> &other) ENTT_NOEXCEPT {
-        resource = other.resource;
-        return *this;
-    }
-
-    /**
-     * @brief Move assignment operator from foreign handle.
-     * @tparam Other Type of resource managed by the received handle.
-     * @param other The handle to move from.
-     * @return This resource handle.
-     */
-    template<typename Other>
-    std::enable_if_t<!std::is_same_v<resource_type, Other> && std::is_base_of_v<resource_type, Other>, resource_handle &>
-    operator=(resource_handle<Other> &&other) ENTT_NOEXCEPT {
-        resource = std::move(other.resource);
-        return *this;
-    }
-
-    /**
-     * @brief Gets a reference to the managed resource.
-     *
-     * @warning
-     * The behavior is undefined if the handle doesn't contain a resource.
-     *
-     * @return A reference to the managed resource.
-     */
-    [[nodiscard]] resource_type &get() const ENTT_NOEXCEPT {
-        return *resource;
-    }
-
-    /*! @copydoc get */
-    [[nodiscard]] operator resource_type &() const ENTT_NOEXCEPT {
-        return get();
-    }
-
-    /*! @copydoc get */
-    [[nodiscard]] resource_type &operator*() const ENTT_NOEXCEPT {
-        return get();
-    }
-
-    /**
-     * @brief Gets a pointer to the managed resource.
-     *
-     * @warning
-     * The behavior is undefined if the handle doesn't contain a resource.
-     *
-     * @return A pointer to the managed resource or `nullptr` if the handle
-     * contains no resource at all.
-     */
-    [[nodiscard]] resource_type *operator->() const ENTT_NOEXCEPT {
-        return resource.get();
-    }
-
-    /**
-     * @brief Returns true if a handle contains a resource, false otherwise.
-     * @return True if the handle contains a resource, false otherwise.
-     */
-    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
-        return static_cast<bool>(resource);
-    }
-
-    /**
-     * @brief Returns the number of handles pointing the same resource.
-     * @return The number of handles pointing the same resource.
-     */
-    [[nodiscard]] size_type use_count() const ENTT_NOEXCEPT {
-        return resource.use_count();
-    }
-
-private:
-    std::shared_ptr<resource_type> resource;
-};
-
-} // namespace entt
-
-#endif

+ 14 - 42
Dependencies/include/entt/resource/loader.hpp

@@ -1,58 +1,30 @@
 #ifndef ENTT_RESOURCE_LOADER_HPP
 #define ENTT_RESOURCE_LOADER_HPP
 
+#include <memory>
+#include <utility>
 #include "fwd.hpp"
-#include "handle.hpp"
 
 namespace entt {
 
 /**
- * @brief Base class for resource loaders.
- *
- * Resource loaders must inherit from this class and stay true to the CRTP
- * idiom. Moreover, a resource loader must expose a public, const member
- * function named `load` that accepts a variable number of arguments and returns
- * a handle to the resource just created.<br/>
- * As an example:
- *
- * @code{.cpp}
- * struct my_resource {};
- *
- * struct my_loader: entt::resource_loader<my_loader, my_resource> {
- *     resource_handle<my_resource> load(int value) const {
- *         // use the integer value somehow
- *         return std::make_shared<my_resource>();
- *     }
- * };
- * @endcode
- *
- * In general, resource loaders should not have a state or retain data of any
- * type. They should let the cache manage their resources instead.
- *
- * @note
- * Base class and CRTP idiom aren't strictly required with the current
- * implementation. One could argue that a cache can easily work with loaders of
- * any type. However, future changes won't be breaking ones by forcing the use
- * of a base class today and that's why the model is already in its place.
- *
- * @tparam Loader Type of the derived class.
- * @tparam Resource Type of resource for which to use the loader.
+ * @brief Transparent loader for shared resources.
+ * @tparam Type Type of resources created by the loader.
  */
-template<typename Loader, typename Resource>
-class resource_loader {
-    /*! @brief Resource loaders are friends of their caches. */
-    template<typename Other>
-    friend class resource_cache;
+template<typename Type>
+struct resource_loader {
+    /*! @brief Result type. */
+    using result_type = std::shared_ptr<Type>;
 
     /**
-     * @brief Loads the resource and returns it.
-     * @tparam Args Types of arguments for the loader.
-     * @param args Arguments for the loader.
-     * @return The resource just loaded or an empty pointer in case of errors.
+     * @brief Constructs a shared pointer to a resource from its arguments.
+     * @tparam Args Types of arguments to use to construct the resource.
+     * @param args Parameters to use to construct the resource.
+     * @return A shared pointer to a resource of the given type.
      */
     template<typename... Args>
-    [[nodiscard]] resource_handle<Resource> get(Args &&...args) const {
-        return static_cast<const Loader *>(this)->load(std::forward<Args>(args)...);
+    result_type operator()(Args &&...args) const {
+        return std::make_shared<Type>(std::forward<Args>(args)...);
     }
 };
 

+ 245 - 0
Dependencies/include/entt/resource/resource.hpp

@@ -0,0 +1,245 @@
+#ifndef ENTT_RESOURCE_RESOURCE_HPP
+#define ENTT_RESOURCE_RESOURCE_HPP
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include "fwd.hpp"
+
+namespace entt {
+
+/**
+ * @brief Basic resource handle.
+ *
+ * A handle wraps a resource and extends its lifetime. It also shares the same
+ * resource with all other handles constructed from the same element.<br/>
+ * As a rule of thumb, resources should never be copied nor moved. Handles are
+ * the way to go to push references around.
+ *
+ * @tparam Type Type of resource managed by a handle.
+ */
+template<typename Type>
+class resource {
+    template<typename>
+    friend class resource;
+
+    template<typename Other>
+    static constexpr bool is_acceptable_v = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>;
+
+public:
+    /*! @brief Resource type. */
+    using element_type = Type;
+    /*! @brief Handle type. */
+    using handle_type = std::shared_ptr<element_type>;
+
+    /*! @brief Default constructor. */
+    resource() noexcept
+        : value{} {}
+
+    /**
+     * @brief Creates a handle from a weak pointer, namely a resource.
+     * @param res A weak pointer to a resource.
+     */
+    explicit resource(handle_type res) noexcept
+        : value{std::move(res)} {}
+
+    /*! @brief Default copy constructor. */
+    resource(const resource &) noexcept = default;
+
+    /*! @brief Default move constructor. */
+    resource(resource &&) noexcept = default;
+
+    /**
+     * @brief Aliasing constructor.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle with which to share ownership information.
+     * @param res Unrelated and unmanaged resources.
+     */
+    template<typename Other>
+    resource(const resource<Other> &other, element_type &res) noexcept
+        : value{other.value, std::addressof(res)} {}
+
+    /**
+     * @brief Copy constructs a handle which shares ownership of the resource.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to copy from.
+     */
+    template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
+    resource(const resource<Other> &other) noexcept
+        : value{other.value} {}
+
+    /**
+     * @brief Move constructs a handle which takes ownership of the resource.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to move from.
+     */
+    template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
+    resource(resource<Other> &&other) noexcept
+        : value{std::move(other.value)} {}
+
+    /**
+     * @brief Default copy assignment operator.
+     * @return This resource handle.
+     */
+    resource &operator=(const resource &) noexcept = default;
+
+    /**
+     * @brief Default move assignment operator.
+     * @return This resource handle.
+     */
+    resource &operator=(resource &&) noexcept = default;
+
+    /**
+     * @brief Copy assignment operator from foreign handle.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to copy from.
+     * @return This resource handle.
+     */
+    template<typename Other>
+    std::enable_if_t<is_acceptable_v<Other>, resource &>
+    operator=(const resource<Other> &other) noexcept {
+        value = other.value;
+        return *this;
+    }
+
+    /**
+     * @brief Move assignment operator from foreign handle.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to move from.
+     * @return This resource handle.
+     */
+    template<typename Other>
+    std::enable_if_t<is_acceptable_v<Other>, resource &>
+    operator=(resource<Other> &&other) noexcept {
+        value = std::move(other.value);
+        return *this;
+    }
+
+    /**
+     * @brief Returns a reference to the managed resource.
+     *
+     * @warning
+     * The behavior is undefined if the handle doesn't contain a resource.
+     *
+     * @return A reference to the managed resource.
+     */
+    [[nodiscard]] element_type &operator*() const noexcept {
+        return *value;
+    }
+
+    /*! @copydoc operator* */
+    [[nodiscard]] operator element_type &() const noexcept {
+        return *value;
+    }
+
+    /**
+     * @brief Returns a pointer to the managed resource.
+     * @return A pointer to the managed resource.
+     */
+    [[nodiscard]] element_type *operator->() const noexcept {
+        return value.get();
+    }
+
+    /**
+     * @brief Returns true if a handle contains a resource, false otherwise.
+     * @return True if the handle contains a resource, false otherwise.
+     */
+    [[nodiscard]] explicit operator bool() const noexcept {
+        return static_cast<bool>(value);
+    }
+
+    /**
+     * @brief Returns the underlying resource handle.
+     * @return The underlying resource handle.
+     */
+    [[nodiscard]] const handle_type &handle() const noexcept {
+        return value;
+    }
+
+private:
+    handle_type value;
+};
+
+/**
+ * @brief Compares two handles.
+ * @tparam Lhs Type of resource managed by the first handle.
+ * @tparam Rhs Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return True if both handles refer to the same resource, false otherwise.
+ */
+template<typename Lhs, typename Rhs>
+[[nodiscard]] bool operator==(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
+    return (std::addressof(*lhs) == std::addressof(*rhs));
+}
+
+/**
+ * @brief Compares two handles.
+ * @tparam Lhs Type of resource managed by the first handle.
+ * @tparam Rhs Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return False if both handles refer to the same resource, true otherwise.
+ */
+template<typename Lhs, typename Rhs>
+[[nodiscard]] bool operator!=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two handles.
+ * @tparam Lhs Type of resource managed by the first handle.
+ * @tparam Rhs Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return True if the first handle is less than the second, false otherwise.
+ */
+template<typename Lhs, typename Rhs>
+[[nodiscard]] bool operator<(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
+    return (std::addressof(*lhs) < std::addressof(*rhs));
+}
+
+/**
+ * @brief Compares two handles.
+ * @tparam Lhs Type of resource managed by the first handle.
+ * @tparam Rhs Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return True if the first handle is greater than the second, false otherwise.
+ */
+template<typename Lhs, typename Rhs>
+[[nodiscard]] bool operator>(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
+    return rhs < lhs;
+}
+
+/**
+ * @brief Compares two handles.
+ * @tparam Lhs Type of resource managed by the first handle.
+ * @tparam Rhs Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return True if the first handle is less than or equal to the second, false
+ * otherwise.
+ */
+template<typename Lhs, typename Rhs>
+[[nodiscard]] bool operator<=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
+    return !(lhs > rhs);
+}
+
+/**
+ * @brief Compares two handles.
+ * @tparam Lhs Type of resource managed by the first handle.
+ * @tparam Rhs Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return True if the first handle is greater than or equal to the second,
+ * false otherwise.
+ */
+template<typename Lhs, typename Rhs>
+[[nodiscard]] bool operator>=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
+    return !(lhs < rhs);
+}
+
+} // namespace entt
+
+#endif

+ 69 - 60
Dependencies/include/entt/signal/delegate.hpp

@@ -8,6 +8,7 @@
 #include <utility>
 #include "../config/config.h"
 #include "../core/type_traits.hpp"
+#include "fwd.hpp"
 
 namespace entt {
 
@@ -19,22 +20,22 @@ namespace entt {
 namespace internal {
 
 template<typename Ret, typename... Args>
-auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
+constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
 
 template<typename Ret, typename Type, typename... Args, typename Other>
-auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
+constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
 
 template<typename Class, typename Ret, typename... Args, typename... Other>
-auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
+constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
 
 template<typename Class, typename Ret, typename... Args, typename... Other>
-auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
+constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
 
 template<typename Class, typename Type, typename... Other>
-auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
+constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
 
 template<typename... Type>
-using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
+using function_pointer_t = decltype(function_pointer(std::declval<Type>()...));
 
 template<typename... Class, typename Ret, typename... Args>
 [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) {
@@ -48,14 +49,6 @@ template<typename... Class, typename Ret, typename... Args>
  * @endcond
  */
 
-/*! @brief Used to wrap a function or a member of a specified type. */
-template<auto>
-struct connect_arg_t {};
-
-/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
-template<auto Func>
-inline constexpr connect_arg_t<Func> connect_arg{};
-
 /**
  * @brief Basic delegate implementation.
  *
@@ -80,28 +73,46 @@ class delegate;
 template<typename Ret, typename... Args>
 class delegate<Ret(Args...)> {
     template<auto Candidate, std::size_t... Index>
-    [[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT {
+    [[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept {
         return [](const void *, Args... args) -> Ret {
             [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
-            return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+
+            if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...>) {
+                return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+            } else {
+                constexpr auto offset = sizeof...(Args) - sizeof...(Index);
+                return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
+            }
         };
     }
 
     template<auto Candidate, typename Type, std::size_t... Index>
-    [[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+    [[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) noexcept {
         return [](const void *payload, Args... args) -> Ret {
             [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
             Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
-            return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+
+            if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...>) {
+                return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+            } else {
+                constexpr auto offset = sizeof...(Args) - sizeof...(Index);
+                return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
+            }
         };
     }
 
     template<auto Candidate, typename Type, std::size_t... Index>
-    [[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+    [[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) noexcept {
         return [](const void *payload, Args... args) -> Ret {
             [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
             Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
-            return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+
+            if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...>) {
+                return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+            } else {
+                constexpr auto offset = sizeof...(Args) - sizeof...(Index);
+                return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
+            }
         };
     }
 
@@ -114,30 +125,19 @@ public:
     using result_type = Ret;
 
     /*! @brief Default constructor. */
-    delegate() ENTT_NOEXCEPT
-        : fn{nullptr},
-          data{nullptr} {}
+    delegate() noexcept
+        : instance{nullptr},
+          fn{nullptr} {}
 
     /**
-     * @brief Constructs a delegate and connects a free function or an unbound
-     * member.
+     * @brief Constructs a delegate with a given object or payload, if any.
      * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload, if any.
+     * @param value_or_instance Optional valid object that fits the purpose.
      */
-    template<auto Candidate>
-    delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT {
-        connect<Candidate>();
-    }
-
-    /**
-     * @brief Constructs a delegate and connects a free function with payload or
-     * a bound member.
-     * @tparam Candidate Function or member to connect to the delegate.
-     * @tparam Type Type of class or type of payload.
-     * @param value_or_instance A valid object that fits the purpose.
-     */
-    template<auto Candidate, typename Type>
-    delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT {
-        connect<Candidate>(std::forward<Type>(value_or_instance));
+    template<auto Candidate, typename... Type>
+    delegate(connect_arg_t<Candidate>, Type &&...value_or_instance) noexcept {
+        connect<Candidate>(std::forward<Type>(value_or_instance)...);
     }
 
     /**
@@ -146,7 +146,7 @@ public:
      * @param function Function to connect to the delegate.
      * @param payload User defined arbitrary data.
      */
-    delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+    delegate(function_type *function, const void *payload = nullptr) noexcept {
         connect(function, payload);
     }
 
@@ -155,8 +155,8 @@ public:
      * @tparam Candidate Function or member to connect to the delegate.
      */
     template<auto Candidate>
-    void connect() ENTT_NOEXCEPT {
-        data = nullptr;
+    void connect() noexcept {
+        instance = nullptr;
 
         if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
             fn = [](const void *, Args... args) -> Ret {
@@ -185,8 +185,8 @@ public:
      * @param value_or_instance A valid reference that fits the purpose.
      */
     template<auto Candidate, typename Type>
-    void connect(Type &value_or_instance) ENTT_NOEXCEPT {
-        data = &value_or_instance;
+    void connect(Type &value_or_instance) noexcept {
+        instance = &value_or_instance;
 
         if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
             fn = [](const void *payload, Args... args) -> Ret {
@@ -209,8 +209,8 @@ public:
      * @param value_or_instance A valid pointer that fits the purpose.
      */
     template<auto Candidate, typename Type>
-    void connect(Type *value_or_instance) ENTT_NOEXCEPT {
-        data = value_or_instance;
+    void connect(Type *value_or_instance) noexcept {
+        instance = value_or_instance;
 
         if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
             fn = [](const void *payload, Args... args) -> Ret {
@@ -235,9 +235,10 @@ public:
      * @param function Function to connect to the delegate.
      * @param payload User defined arbitrary data.
      */
-    void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+    void connect(function_type *function, const void *payload = nullptr) noexcept {
+        ENTT_ASSERT(function != nullptr, "Uninitialized function pointer");
+        instance = payload;
         fn = function;
-        data = payload;
     }
 
     /**
@@ -245,17 +246,25 @@ public:
      *
      * After a reset, a delegate cannot be invoked anymore.
      */
-    void reset() ENTT_NOEXCEPT {
+    void reset() noexcept {
+        instance = nullptr;
         fn = nullptr;
-        data = nullptr;
+    }
+
+    /**
+     * @brief Returns a pointer to the stored callable function target, if any.
+     * @return An opaque pointer to the stored callable function target.
+     */
+    [[nodiscard]] function_type *target() const noexcept {
+        return fn;
     }
 
     /**
      * @brief Returns the instance or the payload linked to a delegate, if any.
      * @return An opaque pointer to the underlying data.
      */
-    [[nodiscard]] const void *instance() const ENTT_NOEXCEPT {
-        return data;
+    [[nodiscard]] const void *data() const noexcept {
+        return instance;
     }
 
     /**
@@ -272,15 +281,15 @@ public:
      */
     Ret operator()(Args... args) const {
         ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate");
-        return fn(data, std::forward<Args>(args)...);
+        return fn(instance, std::forward<Args>(args)...);
     }
 
     /**
      * @brief Checks whether a delegate actually stores a listener.
      * @return False if the delegate is empty, true otherwise.
      */
-    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
-        // no need to test also data
+    [[nodiscard]] explicit operator bool() const noexcept {
+        // no need to also test instance
         return !(fn == nullptr);
     }
 
@@ -289,13 +298,13 @@ public:
      * @param other Delegate with which to compare.
      * @return False if the two contents differ, true otherwise.
      */
-    [[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
-        return fn == other.fn && data == other.data;
+    [[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const noexcept {
+        return fn == other.fn && instance == other.instance;
     }
 
 private:
+    const void *instance;
     function_type *fn;
-    const void *data;
 };
 
 /**
@@ -307,7 +316,7 @@ private:
  * @return True if the two contents differ, false otherwise.
  */
 template<typename Ret, typename... Args>
-[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
+[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) noexcept {
     return !(lhs == rhs);
 }
 

+ 270 - 141
Dependencies/include/entt/signal/dispatcher.hpp

@@ -2,187 +2,328 @@
 #define ENTT_SIGNAL_DISPATCHER_HPP
 
 #include <cstddef>
+#include <functional>
 #include <memory>
 #include <type_traits>
 #include <utility>
 #include <vector>
-#include "../config/config.h"
-#include "../container/dense_hash_map.hpp"
+#include "../container/dense_map.hpp"
+#include "../core/compressed_pair.hpp"
 #include "../core/fwd.hpp"
 #include "../core/type_info.hpp"
 #include "../core/utility.hpp"
+#include "fwd.hpp"
 #include "sigh.hpp"
 
 namespace entt {
 
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct basic_dispatcher_handler {
+    virtual ~basic_dispatcher_handler() = default;
+    virtual void publish() = 0;
+    virtual void disconnect(void *) = 0;
+    virtual void clear() noexcept = 0;
+    virtual std::size_t size() const noexcept = 0;
+};
+
+template<typename Type, typename Allocator>
+class dispatcher_handler final: public basic_dispatcher_handler {
+    static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Invalid type");
+
+    using alloc_traits = std::allocator_traits<Allocator>;
+    using signal_type = sigh<void(Type &), Allocator>;
+    using container_type = std::vector<Type, typename alloc_traits::template rebind_alloc<Type>>;
+
+public:
+    using allocator_type = Allocator;
+
+    dispatcher_handler(const allocator_type &allocator)
+        : signal{allocator},
+          events{allocator} {}
+
+    void publish() override {
+        const auto length = events.size();
+
+        for(std::size_t pos{}; pos < length; ++pos) {
+            signal.publish(events[pos]);
+        }
+
+        events.erase(events.cbegin(), events.cbegin() + length);
+    }
+
+    void disconnect(void *instance) override {
+        bucket().disconnect(instance);
+    }
+
+    void clear() noexcept override {
+        events.clear();
+    }
+
+    [[nodiscard]] auto bucket() noexcept {
+        return typename signal_type::sink_type{signal};
+    }
+
+    void trigger(Type event) {
+        signal.publish(event);
+    }
+
+    template<typename... Args>
+    void enqueue(Args &&...args) {
+        if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) {
+            events.push_back(Type{std::forward<Args>(args)...});
+        } else {
+            events.emplace_back(std::forward<Args>(args)...);
+        }
+    }
+
+    std::size_t size() const noexcept override {
+        return events.size();
+    }
+
+private:
+    signal_type signal;
+    container_type events;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
 /**
  * @brief Basic dispatcher implementation.
  *
  * A dispatcher can be used either to trigger an immediate event or to enqueue
  * events to be published all together once per tick.<br/>
  * Listeners are provided in the form of member functions. For each event of
- * type `Event`, listeners are such that they can be invoked with an argument of
- * type `Event &`, no matter what the return type is.
+ * type `Type`, listeners are such that they can be invoked with an argument of
+ * type `Type &`, no matter what the return type is.
  *
  * The dispatcher creates instances of the `sigh` class internally. Refer to the
  * documentation of the latter for more details.
+ *
+ * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-class dispatcher {
-    struct basic_pool {
-        virtual ~basic_pool() = default;
-        virtual void publish() = 0;
-        virtual void disconnect(void *) = 0;
-        virtual void clear() ENTT_NOEXCEPT = 0;
-    };
-
-    template<typename Event>
-    struct pool_handler final: basic_pool {
-        static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
-
-        using signal_type = sigh<void(Event &)>;
-        using sink_type = typename signal_type::sink_type;
-
-        void publish() override {
-            const auto length = events.size();
+template<typename Allocator>
+class basic_dispatcher {
+    template<typename Type>
+    using handler_type = internal::dispatcher_handler<Type, Allocator>;
 
-            for(std::size_t pos{}; pos < length; ++pos) {
-                signal.publish(events[pos]);
-            }
+    using key_type = id_type;
+    // std::shared_ptr because of its type erased allocator which is useful here
+    using mapped_type = std::shared_ptr<internal::basic_dispatcher_handler>;
 
-            events.erase(events.cbegin(), events.cbegin() + length);
-        }
+    using alloc_traits = std::allocator_traits<Allocator>;
+    using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
+    using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<key_type>, container_allocator>;
 
-        void disconnect(void *instance) override {
-            bucket().disconnect(instance);
-        }
+    template<typename Type>
+    [[nodiscard]] handler_type<Type> &assure(const id_type id) {
+        static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
+        auto &&ptr = pools.first()[id];
 
-        void clear() ENTT_NOEXCEPT override {
-            events.clear();
+        if(!ptr) {
+            const auto &allocator = get_allocator();
+            ptr = std::allocate_shared<handler_type<Type>>(allocator, allocator);
         }
 
-        [[nodiscard]] sink_type bucket() ENTT_NOEXCEPT {
-            return sink_type{signal};
-        }
+        return static_cast<handler_type<Type> &>(*ptr);
+    }
 
-        template<typename... Args>
-        void trigger(Args &&...args) {
-            Event instance{std::forward<Args>(args)...};
-            signal.publish(instance);
-        }
+    template<typename Type>
+    [[nodiscard]] const handler_type<Type> *assure(const id_type id) const {
+        static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
 
-        template<typename... Args>
-        void enqueue(Args &&...args) {
-            if constexpr(std::is_aggregate_v<Event>) {
-                events.push_back(Event{std::forward<Args>(args)...});
-            } else {
-                events.emplace_back(std::forward<Args>(args)...);
-            }
+        if(auto it = pools.first().find(id); it != pools.first().cend()) {
+            return static_cast<const handler_type<Type> *>(it->second.get());
         }
 
-    private:
-        signal_type signal{};
-        std::vector<Event> events;
-    };
-
-    template<typename Event>
-    [[nodiscard]] pool_handler<Event> &assure() {
-        if(auto &&ptr = pools[type_hash<Event>::value()]; !ptr) {
-            auto *cpool = new pool_handler<Event>{};
-            ptr.reset(cpool);
-            return *cpool;
-        } else {
-            return static_cast<pool_handler<Event> &>(*ptr);
-        }
+        return nullptr;
     }
 
 public:
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+
     /*! @brief Default constructor. */
-    dispatcher() = default;
+    basic_dispatcher()
+        : basic_dispatcher{allocator_type{}} {}
 
-    /*! @brief Default move constructor. */
-    dispatcher(dispatcher &&) = default;
+    /**
+     * @brief Constructs a dispatcher with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit basic_dispatcher(const allocator_type &allocator)
+        : pools{allocator, allocator} {}
 
-    /*! @brief Default move assignment operator. @return This dispatcher. */
-    dispatcher &operator=(dispatcher &&) = default;
+    /**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+    basic_dispatcher(basic_dispatcher &&other) noexcept
+        : pools{std::move(other.pools)} {}
 
     /**
-     * @brief Returns a sink object for the given event.
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept
+        : pools{container_type{std::move(other.pools.first()), allocator}, allocator} {
+        ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
+    }
+
+    /**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This dispatcher.
+     */
+    basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
+        ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
+        pools = std::move(other.pools);
+        return *this;
+    }
+
+    /**
+     * @brief Exchanges the contents with those of a given dispatcher.
+     * @param other Dispatcher to exchange the content with.
+     */
+    void swap(basic_dispatcher &other) {
+        using std::swap;
+        swap(pools, other.pools);
+    }
+
+    /**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return pools.second();
+    }
+
+    /**
+     * @brief Returns the number of pending events for a given type.
+     * @tparam Type Type of event for which to return the count.
+     * @param id Name used to map the event queue within the dispatcher.
+     * @return The number of pending events for the given type.
+     */
+    template<typename Type>
+    size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
+        const auto *cpool = assure<std::decay_t<Type>>(id);
+        return cpool ? cpool->size() : 0u;
+    }
+
+    /**
+     * @brief Returns the total number of pending events.
+     * @return The total number of pending events.
+     */
+    size_type size() const noexcept {
+        size_type count{};
+
+        for(auto &&cpool: pools.first()) {
+            count += cpool.second->size();
+        }
+
+        return count;
+    }
+
+    /**
+     * @brief Returns a sink object for the given event and queue.
      *
      * A sink is an opaque object used to connect listeners to events.
      *
      * The function type for a listener is _compatible_ with:
+     *
      * @code{.cpp}
-     * void(Event &);
+     * void(Type &);
      * @endcode
      *
      * The order of invocation of the listeners isn't guaranteed.
      *
      * @sa sink
      *
-     * @tparam Event Type of event of which to get the sink.
+     * @tparam Type Type of event of which to get the sink.
+     * @param id Name used to map the event queue within the dispatcher.
      * @return A temporary sink object.
      */
-    template<typename Event>
-    [[nodiscard]] auto sink() {
-        return assure<Event>().bucket();
+    template<typename Type>
+    [[nodiscard]] auto sink(const id_type id = type_hash<Type>::value()) {
+        return assure<Type>(id).bucket();
     }
 
     /**
-     * @brief Triggers an immediate event of the given type.
-     *
-     * All the listeners registered for the given type are immediately notified.
-     * The event is discarded after the execution.
-     *
-     * @tparam Event Type of event to trigger.
+     * @brief Triggers an immediate event of a given type.
+     * @tparam Type Type of event to trigger.
+     * @param value An instance of the given type of event.
+     */
+    template<typename Type>
+    void trigger(Type &&value = {}) {
+        trigger(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
+    }
+
+    /**
+     * @brief Triggers an immediate event on a queue of a given type.
+     * @tparam Type Type of event to trigger.
+     * @param value An instance of the given type of event.
+     * @param id Name used to map the event queue within the dispatcher.
+     */
+    template<typename Type>
+    void trigger(const id_type id, Type &&value = {}) {
+        assure<std::decay_t<Type>>(id).trigger(std::forward<Type>(value));
+    }
+
+    /**
+     * @brief Enqueues an event of the given type.
+     * @tparam Type Type of event to enqueue.
      * @tparam Args Types of arguments to use to construct the event.
      * @param args Arguments to use to construct the event.
      */
-    template<typename Event, typename... Args>
-    void trigger(Args &&...args) {
-        assure<Event>().trigger(std::forward<Args>(args)...);
+    template<typename Type, typename... Args>
+    void enqueue(Args &&...args) {
+        enqueue_hint<Type>(type_hash<Type>::value(), std::forward<Args>(args)...);
     }
 
     /**
-     * @brief Triggers an immediate event of the given type.
-     *
-     * All the listeners registered for the given type are immediately notified.
-     * The event is discarded after the execution.
-     *
-     * @tparam Event Type of event to trigger.
-     * @param event An instance of the given type of event.
+     * @brief Enqueues an event of the given type.
+     * @tparam Type Type of event to enqueue.
+     * @param value An instance of the given type of event.
      */
-    template<typename Event>
-    void trigger(Event &&event) {
-        assure<std::decay_t<Event>>().trigger(std::forward<Event>(event));
+    template<typename Type>
+    void enqueue(Type &&value) {
+        enqueue_hint(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
     }
 
     /**
      * @brief Enqueues an event of the given type.
-     *
-     * An event of the given type is queued. No listener is invoked. Use the
-     * `update` member function to notify listeners when ready.
-     *
-     * @tparam Event Type of event to enqueue.
+     * @tparam Type Type of event to enqueue.
      * @tparam Args Types of arguments to use to construct the event.
+     * @param id Name used to map the event queue within the dispatcher.
      * @param args Arguments to use to construct the event.
      */
-    template<typename Event, typename... Args>
-    void enqueue(Args &&...args) {
-        assure<Event>().enqueue(std::forward<Args>(args)...);
+    template<typename Type, typename... Args>
+    void enqueue_hint(const id_type id, Args &&...args) {
+        assure<Type>(id).enqueue(std::forward<Args>(args)...);
     }
 
     /**
      * @brief Enqueues an event of the given type.
-     *
-     * An event of the given type is queued. No listener is invoked. Use the
-     * `update` member function to notify listeners when ready.
-     *
-     * @tparam Event Type of event to enqueue.
-     * @param event An instance of the given type of event.
+     * @tparam Type Type of event to enqueue.
+     * @param id Name used to map the event queue within the dispatcher.
+     * @param value An instance of the given type of event.
      */
-    template<typename Event>
-    void enqueue(Event &&event) {
-        assure<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
+    template<typename Type>
+    void enqueue_hint(const id_type id, Type &&value) {
+        assure<std::decay_t<Type>>(id).enqueue(std::forward<Type>(value));
     }
 
     /**
@@ -204,59 +345,47 @@ public:
      */
     template<typename Type>
     void disconnect(Type *value_or_instance) {
-        for(auto &&cpool: pools) {
+        for(auto &&cpool: pools.first()) {
             cpool.second->disconnect(value_or_instance);
         }
     }
 
     /**
-     * @brief Discards all the events queued so far.
-     *
-     * If no types are provided, the dispatcher will clear all the existing
-     * pools.
-     *
-     * @tparam Event Type of events to discard.
+     * @brief Discards all the events stored so far in a given queue.
+     * @tparam Type Type of event to discard.
+     * @param id Name used to map the event queue within the dispatcher.
      */
-    template<typename... Event>
-    void clear() {
-        if constexpr(sizeof...(Event) == 0) {
-            for(auto &&cpool: pools) {
-                cpool.second->clear();
-            }
-        } else {
-            (assure<Event>().clear(), ...);
+    template<typename Type>
+    void clear(const id_type id = type_hash<Type>::value()) {
+        assure<Type>(id).clear();
+    }
+
+    /*! @brief Discards all the events queued so far. */
+    void clear() noexcept {
+        for(auto &&cpool: pools.first()) {
+            cpool.second->clear();
         }
     }
 
     /**
-     * @brief Delivers all the pending events of the given type.
-     *
-     * This method is blocking and it doesn't return until all the events are
-     * delivered to the registered listeners. It's responsibility of the users
-     * to reduce at a minimum the time spent in the bodies of the listeners.
-     *
-     * @tparam Event Type of events to send.
+     * @brief Delivers all the pending events of a given queue.
+     * @tparam Type Type of event to send.
+     * @param id Name used to map the event queue within the dispatcher.
      */
-    template<typename Event>
-    void update() {
-        assure<Event>().publish();
+    template<typename Type>
+    void update(const id_type id = type_hash<Type>::value()) {
+        assure<Type>(id).publish();
     }
 
-    /**
-     * @brief Delivers all the pending events.
-     *
-     * This method is blocking and it doesn't return until all the events are
-     * delivered to the registered listeners. It's responsibility of the users
-     * to reduce at a minimum the time spent in the bodies of the listeners.
-     */
+    /*! @brief Delivers all the pending events. */
     void update() const {
-        for(auto &&cpool: pools) {
+        for(auto &&cpool: pools.first()) {
             cpool.second->publish();
         }
     }
 
 private:
-    dense_hash_map<id_type, std::unique_ptr<basic_pool>, identity> pools;
+    compressed_pair<container_type, allocator_type> pools;
 };
 
 } // namespace entt

+ 95 - 237
Dependencies/include/entt/signal/emitter.hpp

@@ -1,26 +1,22 @@
 #ifndef ENTT_SIGNAL_EMITTER_HPP
 #define ENTT_SIGNAL_EMITTER_HPP
 
-#include <algorithm>
 #include <functional>
-#include <iterator>
-#include <list>
-#include <memory>
 #include <type_traits>
 #include <utility>
-#include "../config/config.h"
-#include "../container/dense_hash_map.hpp"
+#include "../container/dense_map.hpp"
+#include "../core/compressed_pair.hpp"
 #include "../core/fwd.hpp"
 #include "../core/type_info.hpp"
 #include "../core/utility.hpp"
+#include "fwd.hpp"
 
 namespace entt {
 
 /**
  * @brief General purpose event emitter.
  *
- * The emitter class template follows the CRTP idiom. To create a custom emitter
- * type, derived classes must inherit directly from the base class as:
+ * To create an emitter type, derived classes must inherit from the base as:
  *
  * @code{.cpp}
  * struct my_emitter: emitter<my_emitter> {
@@ -28,287 +24,149 @@ namespace entt {
  * }
  * @endcode
  *
- * Pools for the type of events are created internally on the fly. It's not
- * required to specify in advance the full list of accepted types.<br/>
- * Moreover, whenever an event is published, an emitter provides the listeners
- * with a reference to itself along with a reference to the event. Therefore
- * listeners have an handy way to work with it without incurring in the need of
- * capturing a reference to the emitter.
+ * Handlers for the different events are created internally on the fly. It's not
+ * required to specify in advance the full list of accepted events.<br/>
+ * Moreover, whenever an event is published, an emitter also passes a reference
+ * to itself to its listeners.
  *
- * @tparam Derived Actual type of emitter that extends the class template.
+ * @tparam Derived Emitter type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Derived>
+template<typename Derived, typename Allocator>
 class emitter {
-    struct basic_pool {
-        virtual ~basic_pool() = default;
-        virtual bool empty() const ENTT_NOEXCEPT = 0;
-        virtual void clear() ENTT_NOEXCEPT = 0;
-    };
+    using key_type = id_type;
+    using mapped_type = std::function<void(void *)>;
 
-    template<typename Event>
-    struct pool_handler final: basic_pool {
-        static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
-
-        using listener_type = std::function<void(Event &, Derived &)>;
-        using element_type = std::pair<bool, listener_type>;
-        using container_type = std::list<element_type>;
-        using connection_type = typename container_type::iterator;
-
-        [[nodiscard]] bool empty() const ENTT_NOEXCEPT override {
-            auto pred = [](auto &&element) { return element.first; };
-
-            return std::all_of(once_list.cbegin(), once_list.cend(), pred)
-                   && std::all_of(on_list.cbegin(), on_list.cend(), pred);
-        }
-
-        void clear() ENTT_NOEXCEPT override {
-            if(publishing) {
-                for(auto &&element: once_list) {
-                    element.first = true;
-                }
-
-                for(auto &&element: on_list) {
-                    element.first = true;
-                }
-            } else {
-                once_list.clear();
-                on_list.clear();
-            }
-        }
-
-        connection_type once(listener_type listener) {
-            return once_list.emplace(once_list.cend(), false, std::move(listener));
-        }
-
-        connection_type on(listener_type listener) {
-            return on_list.emplace(on_list.cend(), false, std::move(listener));
-        }
-
-        void erase(connection_type conn) {
-            conn->first = true;
-
-            if(!publishing) {
-                auto pred = [](auto &&element) { return element.first; };
-                once_list.remove_if(pred);
-                on_list.remove_if(pred);
-            }
-        }
-
-        void publish(Event &event, Derived &ref) {
-            container_type swap_list;
-            once_list.swap(swap_list);
-
-            publishing = true;
-
-            for(auto &&element: on_list) {
-                element.first ? void() : element.second(event, ref);
-            }
-
-            for(auto &&element: swap_list) {
-                element.first ? void() : element.second(event, ref);
-            }
-
-            publishing = false;
-
-            on_list.remove_if([](auto &&element) { return element.first; });
-        }
-
-    private:
-        bool publishing{false};
-        container_type once_list{};
-        container_type on_list{};
-    };
-
-    template<typename Event>
-    [[nodiscard]] pool_handler<Event> *assure() {
-        if(auto &&ptr = pools[type_hash<Event>::value()]; !ptr) {
-            auto *cpool = new pool_handler<Event>{};
-            ptr.reset(cpool);
-            return cpool;
-        } else {
-            return static_cast<pool_handler<Event> *>(ptr.get());
-        }
-    }
-
-    template<typename Event>
-    [[nodiscard]] const pool_handler<Event> *assure() const {
-        const auto it = pools.find(type_hash<Event>::value());
-        return (it == pools.cend()) ? nullptr : static_cast<const pool_handler<Event> *>(it->second.get());
-    }
+    using alloc_traits = std::allocator_traits<Allocator>;
+    using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
+    using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<key_type>, container_allocator>;
 
 public:
-    /** @brief Type of listeners accepted for the given event. */
-    template<typename Event>
-    using listener = typename pool_handler<Event>::listener_type;
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+
+    /*! @brief Default constructor. */
+    emitter()
+        : emitter{allocator_type{}} {}
 
     /**
-     * @brief Generic connection type for events.
-     *
-     * Type of the connection object returned by the event emitter whenever a
-     * listener for the given type is registered.<br/>
-     * It can be used to break connections still in use.
-     *
-     * @tparam Event Type of event for which the connection is created.
+     * @brief Constructs an emitter with a given allocator.
+     * @param allocator The allocator to use.
      */
-    template<typename Event>
-    struct connection: private pool_handler<Event>::connection_type {
-        /** @brief Event emitters are friend classes of connections. */
-        friend class emitter;
-
-        /*! @brief Default constructor. */
-        connection() = default;
-
-        /**
-         * @brief Creates a connection that wraps its underlying instance.
-         * @param conn A connection object to wrap.
-         */
-        connection(typename pool_handler<Event>::connection_type conn)
-            : pool_handler<Event>::connection_type{std::move(conn)} {}
-    };
-
-    /*! @brief Default constructor. */
-    emitter() = default;
+    explicit emitter(const allocator_type &allocator)
+        : handlers{allocator, allocator} {}
 
     /*! @brief Default destructor. */
-    virtual ~emitter() {
-        static_assert(std::is_base_of_v<emitter<Derived>, Derived>, "Incorrect use of the class template");
+    virtual ~emitter() noexcept {
+        static_assert(std::is_base_of_v<emitter<Derived, Allocator>, Derived>, "Invalid emitter type");
     }
 
-    /*! @brief Default move constructor. */
-    emitter(emitter &&) = default;
+    /**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+    emitter(emitter &&other) noexcept
+        : handlers{std::move(other.handlers)} {}
 
-    /*! @brief Default move assignment operator. @return This emitter. */
-    emitter &operator=(emitter &&) = default;
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    emitter(emitter &&other, const allocator_type &allocator) noexcept
+        : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {
+        ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
+    }
 
     /**
-     * @brief Emits the given event.
-     *
-     * All the listeners registered for the specific event type are invoked with
-     * the given event. The event type must either have a proper constructor for
-     * the arguments provided or be an aggregate type.
-     *
-     * @tparam Event Type of event to publish.
-     * @tparam Args Types of arguments to use to construct the event.
-     * @param args Parameters to use to initialize the event.
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This dispatcher.
      */
-    template<typename Event, typename... Args>
-    void publish(Args &&...args) {
-        Event instance{std::forward<Args>(args)...};
-        assure<Event>()->publish(instance, *static_cast<Derived *>(this));
+    emitter &operator=(emitter &&other) noexcept {
+        ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
+
+        handlers = std::move(other.handlers);
+        return *this;
     }
 
     /**
-     * @brief Registers a long-lived listener with the event emitter.
-     *
-     * This method can be used to register a listener designed to be invoked
-     * more than once for the given event type.<br/>
-     * The connection returned by the method can be freely discarded. It's meant
-     * to be used later to disconnect the listener if required.
-     *
-     * The listener is as a callable object that can be moved and the type of
-     * which is _compatible_ with `void(Event &, Derived &)`.
-     *
-     * @note
-     * Whenever an event is emitted, the emitter provides the listener with a
-     * reference to the derived class. Listeners don't have to capture those
-     * instances for later uses.
-     *
-     * @tparam Event Type of event to which to connect the listener.
-     * @param instance The listener to register.
-     * @return Connection object that can be used to disconnect the listener.
+     * @brief Exchanges the contents with those of a given emitter.
+     * @param other Emitter to exchange the content with.
      */
-    template<typename Event>
-    connection<Event> on(listener<Event> instance) {
-        return assure<Event>()->on(std::move(instance));
+    void swap(emitter &other) {
+        using std::swap;
+        swap(handlers, other.handlers);
     }
 
     /**
-     * @brief Registers a short-lived listener with the event emitter.
-     *
-     * This method can be used to register a listener designed to be invoked
-     * only once for the given event type.<br/>
-     * The connection returned by the method can be freely discarded. It's meant
-     * to be used later to disconnect the listener if required.
-     *
-     * The listener is as a callable object that can be moved and the type of
-     * which is _compatible_ with `void(Event &, Derived &)`.
-     *
-     * @note
-     * Whenever an event is emitted, the emitter provides the listener with a
-     * reference to the derived class. Listeners don't have to capture those
-     * instances for later uses.
-     *
-     * @tparam Event Type of event to which to connect the listener.
-     * @param instance The listener to register.
-     * @return Connection object that can be used to disconnect the listener.
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
      */
-    template<typename Event>
-    connection<Event> once(listener<Event> instance) {
-        return assure<Event>()->once(std::move(instance));
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return handlers.second();
     }
 
     /**
-     * @brief Disconnects a listener from the event emitter.
-     *
-     * Do not use twice the same connection to disconnect a listener, it results
-     * in undefined behavior. Once used, discard the connection object.
-     *
-     * @tparam Event Type of event of the connection.
-     * @param conn A valid connection.
+     * @brief Publishes a given event.
+     * @tparam Type Type of event to trigger.
+     * @param value An instance of the given type of event.
      */
-    template<typename Event>
-    void erase(connection<Event> conn) {
-        assure<Event>()->erase(std::move(conn));
+    template<typename Type>
+    void publish(Type &&value) {
+        if(const auto id = type_id<Type>().hash(); handlers.first().contains(id)) {
+            handlers.first()[id](&value);
+        }
     }
 
     /**
-     * @brief Disconnects all the listeners for the given event type.
-     *
-     * All the connections previously returned for the given event are
-     * invalidated. Using them results in undefined behavior.
-     *
-     * @tparam Event Type of event to reset.
+     * @brief Registers a listener with the event emitter.
+     * @tparam Type Type of event to which to connect the listener.
+     * @param func The listener to register.
      */
-    template<typename Event>
-    void clear() {
-        assure<Event>()->clear();
+    template<typename Type>
+    void on(std::function<void(Type &, Derived &)> func) {
+        handlers.first().insert_or_assign(type_id<Type>().hash(), [func = std::move(func), this](void *value) {
+            func(*static_cast<Type *>(value), static_cast<Derived &>(*this));
+        });
     }
 
     /**
-     * @brief Disconnects all the listeners.
-     *
-     * All the connections previously returned are invalidated. Using them
-     * results in undefined behavior.
+     * @brief Disconnects a listener from the event emitter.
+     * @tparam Type Type of event of the listener.
      */
-    void clear() ENTT_NOEXCEPT {
-        for(auto &&cpool: pools) {
-            cpool.second->clear();
-        }
+    template<typename Type>
+    void erase() {
+        handlers.first().erase(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
+    }
+
+    /*! @brief Disconnects all the listeners. */
+    void clear() noexcept {
+        handlers.first().clear();
     }
 
     /**
      * @brief Checks if there are listeners registered for the specific event.
-     * @tparam Event Type of event to test.
+     * @tparam Type Type of event to test.
      * @return True if there are no listeners registered, false otherwise.
      */
-    template<typename Event>
-    [[nodiscard]] bool empty() const {
-        const auto *cpool = assure<Event>();
-        return !cpool || cpool->empty();
+    template<typename Type>
+    [[nodiscard]] bool contains() const {
+        return handlers.first().contains(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
     }
 
     /**
      * @brief Checks if there are listeners registered with the event emitter.
      * @return True if there are no listeners registered, false otherwise.
      */
-    [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
-        return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) {
-            return cpool.second->empty();
-        });
+    [[nodiscard]] bool empty() const noexcept {
+        return handlers.first().empty();
     }
 
 private:
-    dense_hash_map<id_type, std::unique_ptr<basic_pool>, identity> pools{};
+    compressed_pair<container_type, allocator_type> handlers;
 };
 
 } // namespace entt

+ 21 - 3
Dependencies/include/entt/signal/fwd.hpp

@@ -8,9 +8,10 @@ namespace entt {
 template<typename>
 class delegate;
 
-class dispatcher;
+template<typename = std::allocator<void>>
+class basic_dispatcher;
 
-template<typename>
+template<typename, typename = std::allocator<void>>
 class emitter;
 
 class connection;
@@ -20,9 +21,26 @@ struct scoped_connection;
 template<typename>
 class sink;
 
-template<typename Type, typename = std::allocator<delegate<Type>>>
+template<typename Type, typename = std::allocator<void>>
 class sigh;
 
+/*! @brief Alias declaration for the most common use case. */
+using dispatcher = basic_dispatcher<>;
+
+/*! @brief Disambiguation tag for constructors and the like. */
+template<auto>
+struct connect_arg_t {
+    /*! @brief Default constructor. */
+    explicit connect_arg_t() = default;
+};
+
+/**
+ * @brief Constant of type connect_arg_t used to disambiguate calls.
+ * @tparam Candidate Element to connect (likely a free or member function).
+ */
+template<auto Candidate>
+inline constexpr connect_arg_t<Candidate> connect_arg{};
+
 } // namespace entt
 
 #endif

+ 80 - 222
Dependencies/include/entt/signal/sigh.hpp

@@ -1,13 +1,11 @@
 #ifndef ENTT_SIGNAL_SIGH_HPP
 #define ENTT_SIGNAL_SIGH_HPP
 
-#include <algorithm>
-#include <functional>
-#include <iterator>
+#include <cstddef>
+#include <memory>
 #include <type_traits>
 #include <utility>
 #include <vector>
-#include "../config/config.h"
 #include "delegate.hpp"
 #include "fwd.hpp"
 
@@ -54,14 +52,11 @@ class sigh;
  */
 template<typename Ret, typename... Args, typename Allocator>
 class sigh<Ret(Args...), Allocator> {
-    /*! @brief A sink is allowed to modify a signal. */
     friend class sink<sigh<Ret(Args...), Allocator>>;
 
-    using allocator_traits = std::allocator_traits<Allocator>;
-    using alloc = typename allocator_traits::template rebind_alloc<delegate<Ret(Args...)>>;
-    using alloc_traits = typename std::allocator_traits<alloc>;
-
-    using container_type = std::vector<delegate<Ret(Args...)>, alloc>;
+    using alloc_traits = std::allocator_traits<Allocator>;
+    using delegate_type = delegate<Ret(Args...)>;
+    using container_type = std::vector<delegate_type, typename alloc_traits::template rebind_alloc<delegate_type>>;
 
 public:
     /*! @brief Allocator type. */
@@ -72,21 +67,21 @@ public:
     using sink_type = sink<sigh<Ret(Args...), Allocator>>;
 
     /*! @brief Default constructor. */
-    sigh()
+    sigh() noexcept(std::is_nothrow_default_constructible_v<allocator_type> &&std::is_nothrow_constructible_v<container_type, const allocator_type &>)
         : sigh{allocator_type{}} {}
 
     /**
      * @brief Constructs a signal handler with a given allocator.
      * @param allocator The allocator to use.
      */
-    explicit sigh(const allocator_type &allocator)
+    explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v<container_type, const allocator_type &>)
         : calls{allocator} {}
 
     /**
-     * @brief Default copy constructor.
+     * @brief Copy constructor.
      * @param other The instance to copy from.
      */
-    sigh(const sigh &other)
+    sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v<container_type>)
         : calls{other.calls} {}
 
     /**
@@ -94,14 +89,14 @@ public:
      * @param other The instance to copy from.
      * @param allocator The allocator to use.
      */
-    sigh(const sigh &other, const allocator_type &allocator)
+    sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v<container_type, const container_type &, const allocator_type &>)
         : calls{other.calls, allocator} {}
 
     /**
-     * @brief Default move constructor.
+     * @brief Move constructor.
      * @param other The instance to move from.
      */
-    sigh(sigh &&other) ENTT_NOEXCEPT
+    sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v<container_type>)
         : calls{std::move(other.calls)} {}
 
     /**
@@ -109,25 +104,25 @@ public:
      * @param other The instance to move from.
      * @param allocator The allocator to use.
      */
-    sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+    sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v<container_type, container_type &&, const allocator_type &>)
         : calls{std::move(other.calls), allocator} {}
 
     /**
-     * @brief Default copy assignment operator.
+     * @brief Copy assignment operator.
      * @param other The instance to copy from.
      * @return This signal handler.
      */
-    sigh &operator=(const sigh &other) {
+    sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v<container_type>) {
         calls = other.calls;
         return *this;
     }
 
     /**
-     * @brief Default move assignment operator.
+     * @brief Move assignment operator.
      * @param other The instance to move from.
      * @return This signal handler.
      */
-    sigh &operator=(sigh &&other) ENTT_NOEXCEPT {
+    sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v<container_type>) {
         calls = std::move(other.calls);
         return *this;
     }
@@ -136,7 +131,7 @@ public:
      * @brief Exchanges the contents with those of a given signal handler.
      * @param other Signal handler to exchange the content with.
      */
-    void swap(sigh &other) {
+    void swap(sigh &other) noexcept(std::is_nothrow_swappable_v<container_type>) {
         using std::swap;
         swap(calls, other.calls);
     }
@@ -145,22 +140,15 @@ public:
      * @brief Returns the associated allocator.
      * @return The associated allocator.
      */
-    [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
         return calls.get_allocator();
     }
 
-    /**
-     * @brief Instance type when it comes to connecting member functions.
-     * @tparam Class Type of class to which the member function belongs.
-     */
-    template<typename Class>
-    using instance_type = Class *;
-
     /**
      * @brief Number of listeners connected to the signal.
      * @return Number of listeners currently connected.
      */
-    [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+    [[nodiscard]] size_type size() const noexcept {
         return calls.size();
     }
 
@@ -168,7 +156,7 @@ public:
      * @brief Returns false if at least a listener is connected to the signal.
      * @return True if the signal has no listeners connected, false otherwise.
      */
-    [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+    [[nodiscard]] bool empty() const noexcept {
         return calls.empty();
     }
 
@@ -180,8 +168,8 @@ public:
      * @param args Arguments to use to invoke listeners.
      */
     void publish(Args... args) const {
-        for(auto &&call: std::as_const(calls)) {
-            call(args...);
+        for(auto pos = calls.size(); pos; --pos) {
+            calls[pos - 1u](args...);
         }
     }
 
@@ -201,20 +189,24 @@ public:
      */
     template<typename Func>
     void collect(Func func, Args... args) const {
-        for(auto &&call: calls) {
-            if constexpr(std::is_void_v<Ret>) {
+        for(auto pos = calls.size(); pos; --pos) {
+            if constexpr(std::is_void_v<Ret> || !std::is_invocable_v<Func, Ret>) {
+                calls[pos - 1u](args...);
+
                 if constexpr(std::is_invocable_r_v<bool, Func>) {
-                    call(args...);
-                    if(func()) { break; }
+                    if(func()) {
+                        break;
+                    }
                 } else {
-                    call(args...);
                     func();
                 }
             } else {
                 if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
-                    if(func(call(args...))) { break; }
+                    if(func(calls[pos - 1u](args...))) {
+                        break;
+                    }
                 } else {
-                    func(call(args...));
+                    func(calls[pos - 1u](args...));
                 }
             }
         }
@@ -232,7 +224,6 @@ private:
  * the sink that generated it.
  */
 class connection {
-    /*! @brief A sink is allowed to create connection objects. */
     template<typename>
     friend class sink;
 
@@ -241,13 +232,15 @@ class connection {
 
 public:
     /*! @brief Default constructor. */
-    connection() = default;
+    connection()
+        : disconnect{},
+          signal{} {}
 
     /**
      * @brief Checks whether a connection is properly initialized.
      * @return True if the connection is properly initialized, false otherwise.
      */
-    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+    [[nodiscard]] explicit operator bool() const noexcept {
         return static_cast<bool>(disconnect);
     }
 
@@ -261,7 +254,7 @@ public:
 
 private:
     delegate<void(void *)> disconnect;
-    void *signal{};
+    void *signal;
 };
 
 /**
@@ -291,7 +284,7 @@ struct scoped_connection {
      * @brief Move constructor.
      * @param other The scoped connection to move from.
      */
-    scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT
+    scoped_connection(scoped_connection &&other) noexcept
         : conn{std::exchange(other.conn, {})} {}
 
     /*! @brief Automatically breaks the link on destruction. */
@@ -310,7 +303,7 @@ struct scoped_connection {
      * @param other The scoped connection to move from.
      * @return This scoped connection.
      */
-    scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT {
+    scoped_connection &operator=(scoped_connection &&other) noexcept {
         conn = std::exchange(other.conn, {});
         return *this;
     }
@@ -329,7 +322,7 @@ struct scoped_connection {
      * @brief Checks whether a scoped connection is properly initialized.
      * @return True if the connection is properly initialized, false otherwise.
      */
-    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+    [[nodiscard]] explicit operator bool() const noexcept {
         return static_cast<bool>(conn);
     }
 
@@ -364,7 +357,8 @@ private:
 template<typename Ret, typename... Args, typename Allocator>
 class sink<sigh<Ret(Args...), Allocator>> {
     using signal_type = sigh<Ret(Args...), Allocator>;
-    using difference_type = typename std::iterator_traits<typename decltype(signal_type::calls)::iterator>::difference_type;
+    using delegate_type = typename signal_type::delegate_type;
+    using difference_type = typename signal_type::container_type::difference_type;
 
     template<auto Candidate, typename Type>
     static void release(Type value_or_instance, void *signal) {
@@ -376,210 +370,75 @@ class sink<sigh<Ret(Args...), Allocator>> {
         sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
     }
 
+    template<typename Func>
+    void disconnect_if(Func callback) {
+        for(auto pos = signal->calls.size(); pos; --pos) {
+            if(auto &elem = signal->calls[pos - 1u]; callback(elem)) {
+                elem = std::move(signal->calls.back());
+                signal->calls.pop_back();
+            }
+        }
+    }
+
 public:
     /**
      * @brief Constructs a sink that is allowed to modify a given signal.
      * @param ref A valid reference to a signal object.
      */
-    sink(sigh<Ret(Args...), Allocator> &ref) ENTT_NOEXCEPT
-        : offset{},
-          signal{&ref} {}
+    sink(sigh<Ret(Args...), Allocator> &ref) noexcept
+        : signal{&ref} {}
 
     /**
      * @brief Returns false if at least a listener is connected to the sink.
      * @return True if the sink has no listeners connected, false otherwise.
      */
-    [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+    [[nodiscard]] bool empty() const noexcept {
         return signal->calls.empty();
     }
 
     /**
-     * @brief Returns a sink that connects before a given free function or an
-     * unbound member.
-     * @tparam Function A valid free function pointer.
-     * @return A properly initialized sink object.
-     */
-    template<auto Function>
-    [[nodiscard]] sink before() {
-        delegate<Ret(Args...)> call{};
-        call.template connect<Function>();
-
-        const auto &calls = signal->calls;
-        const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
-
-        sink other{*this};
-        other.offset = std::distance(it, calls.cend());
-        return other;
-    }
-
-    /**
-     * @brief Returns a sink that connects before a free function with payload
-     * or a bound member.
-     * @tparam Candidate Member or free function to look for.
-     * @tparam Type Type of class or type of payload.
-     * @param value_or_instance A valid object that fits the purpose.
-     * @return A properly initialized sink object.
-     */
-    template<auto Candidate, typename Type>
-    [[nodiscard]] sink before(Type &&value_or_instance) {
-        delegate<Ret(Args...)> call{};
-        call.template connect<Candidate>(value_or_instance);
-
-        const auto &calls = signal->calls;
-        const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
-
-        sink other{*this};
-        other.offset = std::distance(it, calls.cend());
-        return other;
-    }
-
-    /**
-     * @brief Returns a sink that connects before a given instance or specific
-     * payload.
-     * @tparam Type Type of class or type of payload.
-     * @param value_or_instance A valid object that fits the purpose.
-     * @return A properly initialized sink object.
-     */
-    template<typename Type>
-    [[nodiscard]] sink before(Type &value_or_instance) {
-        return before(&value_or_instance);
-    }
-
-    /**
-     * @brief Returns a sink that connects before a given instance or specific
-     * payload.
-     * @tparam Type Type of class or type of payload.
-     * @param value_or_instance A valid pointer that fits the purpose.
-     * @return A properly initialized sink object.
-     */
-    template<typename Type>
-    [[nodiscard]] sink before(Type *value_or_instance) {
-        sink other{*this};
-
-        if(value_or_instance) {
-            const auto &calls = signal->calls;
-            const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
-                return delegate.instance() == value_or_instance;
-            });
-
-            other.offset = std::distance(it, calls.cend());
-        }
-
-        return other;
-    }
-
-    /**
-     * @brief Returns a sink that connects before anything else.
-     * @return A properly initialized sink object.
-     */
-    [[nodiscard]] sink before() {
-        sink other{*this};
-        other.offset = signal->calls.size();
-        return other;
-    }
-
-    /**
-     * @brief Connects a free function or an unbound member to a signal.
-     *
-     * The signal handler performs checks to avoid multiple connections for the
-     * same function.
-     *
-     * @tparam Candidate Function or member to connect to the signal.
-     * @return A properly initialized connection object.
-     */
-    template<auto Candidate>
-    connection connect() {
-        disconnect<Candidate>();
-
-        delegate<Ret(Args...)> call{};
-        call.template connect<Candidate>();
-        signal->calls.insert(signal->calls.end() - offset, std::move(call));
-
-        delegate<void(void *)> conn{};
-        conn.template connect<&release<Candidate>>();
-        return {std::move(conn), signal};
-    }
-
-    /**
-     * @brief Connects a free function with payload or a bound member to a
-     * signal.
-     *
-     * The signal isn't responsible for the connected object or the payload.
-     * Users must always guarantee that the lifetime of the instance overcomes
-     * the one of the signal. On the other side, the signal handler performs
-     * checks to avoid multiple connections for the same function.<br/>
-     * When used to connect a free function with payload, its signature must be
-     * such that the instance is the first argument before the ones used to
-     * define the signal itself.
-     *
+     * @brief Connects a free function (with or without payload), a bound or an
+     * unbound member to a signal.
      * @tparam Candidate Function or member to connect to the signal.
-     * @tparam Type Type of class or type of payload.
-     * @param value_or_instance A valid object that fits the purpose.
+     * @tparam Type Type of class or type of payload, if any.
+     * @param value_or_instance A valid object that fits the purpose, if any.
      * @return A properly initialized connection object.
      */
-    template<auto Candidate, typename Type>
-    connection connect(Type &&value_or_instance) {
-        disconnect<Candidate>(value_or_instance);
+    template<auto Candidate, typename... Type>
+    connection connect(Type &&...value_or_instance) {
+        disconnect<Candidate>(value_or_instance...);
 
-        delegate<Ret(Args...)> call{};
-        call.template connect<Candidate>(value_or_instance);
-        signal->calls.insert(signal->calls.end() - offset, std::move(call));
+        delegate_type call{};
+        call.template connect<Candidate>(value_or_instance...);
+        signal->calls.push_back(std::move(call));
 
         delegate<void(void *)> conn{};
-        conn.template connect<&release<Candidate, Type>>(value_or_instance);
+        conn.template connect<&release<Candidate, Type...>>(value_or_instance...);
         return {std::move(conn), signal};
     }
 
     /**
-     * @brief Disconnects a free function or an unbound member from a signal.
-     * @tparam Candidate Function or member to disconnect from the signal.
-     */
-    template<auto Candidate>
-    void disconnect() {
-        auto &calls = signal->calls;
-        delegate<Ret(Args...)> call{};
-        call.template connect<Candidate>();
-        calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
-    }
-
-    /**
-     * @brief Disconnects a free function with payload or a bound member from a
-     * signal.
+     * @brief Disconnects a free function (with or without payload), a bound or
+     * an unbound member from a signal.
      * @tparam Candidate Function or member to disconnect from the signal.
-     * @tparam Type Type of class or type of payload.
-     * @param value_or_instance A valid object that fits the purpose.
-     */
-    template<auto Candidate, typename Type>
-    void disconnect(Type &&value_or_instance) {
-        auto &calls = signal->calls;
-        delegate<Ret(Args...)> call{};
-        call.template connect<Candidate>(value_or_instance);
-        calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
-    }
-
-    /**
-     * @brief Disconnects free functions with payload or bound members from a
-     * signal.
-     * @tparam Type Type of class or type of payload.
-     * @param value_or_instance A valid object that fits the purpose.
-     */
-    template<typename Type>
-    void disconnect(Type &value_or_instance) {
-        disconnect(&value_or_instance);
+     * @tparam Type Type of class or type of payload, if any.
+     * @param value_or_instance A valid object that fits the purpose, if any.
+     */
+    template<auto Candidate, typename... Type>
+    void disconnect(Type &&...value_or_instance) {
+        delegate_type call{};
+        call.template connect<Candidate>(value_or_instance...);
+        disconnect_if([&call](const auto &elem) { return elem == call; });
     }
 
     /**
      * @brief Disconnects free functions with payload or bound members from a
      * signal.
-     * @tparam Type Type of class or type of payload.
      * @param value_or_instance A valid object that fits the purpose.
      */
-    template<typename Type>
-    void disconnect(Type *value_or_instance) {
+    void disconnect(const void *value_or_instance) {
         if(value_or_instance) {
-            auto &calls = signal->calls;
-            auto predicate = [value_or_instance](const auto &delegate) { return delegate.instance() == value_or_instance; };
-            calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end());
+            disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; });
         }
     }
 
@@ -589,7 +448,6 @@ public:
     }
 
 private:
-    difference_type offset;
     signal_type *signal;
 };
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 617 - 324
Praxis3D/Data/Maps/componentTest.pmap


+ 9 - 7
Praxis3D/Data/Prefabs/cube1.prefab

@@ -11,34 +11,36 @@
 	{
 		"ModelComponent":
 		{
-			"Models":
+			"Models": 
 			[
 				{
 					"Filename": "cube.obj",
-					"Meshes":
+					"Meshes": 
 					[
 						{
 							"Index": "0",
+							"Active": "true",
 							"AlphaThreshold": "0.0f",
+							"EmissiveIntensity": "0.0f",
 							"HeightScale": "0.0f",
-							"Materials":
+							"Materials": 
 							{
-								"Diffuse":
+								"Diffuse": 
 								{
 									"Filename": "badlands-boulders_albedo.png",
 									"TextureScale": "2.0f, 2.0f"
 								},
-								"Normal":
+								"Normal": 
 								{
 									"Filename": "badlands-boulders_normal-ogl.png",
 									"TextureScale": "2.0f, 2.0f"
 								},
-								"Emissive":
+								"Emissive": 
 								{
 									"Filename": "default_emissive.png",
 									"TextureScale": "1.0f, 1.0f"
 								},
-								"RMHAO":
+								"RMHAO": 
 								{
 									"Filename": "badlands-boulders_RMHAO.png",
 									"TextureScale": "2.0f, 2.0f"

+ 6 - 23
Praxis3D/Data/Shaders/geometryPass.frag

@@ -1,7 +1,7 @@
 #version 430 core
 
-#define MIN_LOD_PARALLAX 0.0
-#define MAX_LOD_PARALLAX 10.0
+#define MIN_LOD_PARALLAX 0.1
+#define MAX_LOD_PARALLAX 20.0
 #define LOD_PARALLAX_THRESHOLD 0.0
 #define PARALLAX_SCALE_THRESHOLD 0.001
 #define MIN_ROUGHNESS 0.001
@@ -271,7 +271,7 @@ vec2 parallaxOcclusionMapping(vec2 texCoords, vec3 viewDir, float p_LOD)
 	// number of depth layers
     const float minLayers = 15;
     const float maxLayers = 20;
-    float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir))) * p_LOD;  
+    float numLayers = max(mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir))) * p_LOD, 1.0);  
     // calculate the size of each layer
     float layerDepth = 1.0 / numLayers;
     // depth of current layer
@@ -332,29 +332,13 @@ void main(void)
 		float distanceToFrag = length(viewDir);
 		viewDir = normalize(viewDir);
 		
-		float LOD = clamp(1.0 - ((distanceToFrag * distanceToFrag) / parallaxLOD), MIN_LOD_PARALLAX, MAX_LOD_PARALLAX);
+		float LOD = mix(MIN_LOD_PARALLAX, MAX_LOD_PARALLAX, 1.0 - ((distanceToFrag) / parallaxLOD));
 		
-		if(LOD > LOD_PARALLAX_THRESHOLD)
+		//if(LOD > LOD_PARALLAX_THRESHOLD)
 			newCoords = parallaxOcclusionMapping(texCoord, viewDir, LOD);
-			    
-		//if(newCoords.x > 1.0 || newCoords.y > 1.0 || newCoords.x < 0.0 || newCoords.y < 0.0)
-		//	discard;
+			
 	}
 	
-	//if(distanceToFrag < 9.90)
-	//{
-		//float LOD = min(((10.0 - distanceToFrag) / 10.0), 1.0);
-		//float LOD2 = clamp((distanceToFrag - 8.0) / 2.0, 0.0, 1.0);
-	
-		//if(parallaxScale > PARALLAX_SCALE_THRESHOLD)
-		//	newCoords = mix(parallaxOcclusionMapping(texCoord, viewDir, LOD), texCoord, 1.0 - LOD);
-	//}
-	
-	// discards a fragment when sampling outside default texture region (fixes border artifacts)
-    //if(newCoords.x > 1.0 || newCoords.y > 1.0 || newCoords.x < 0.0 || newCoords.y < 0.0)
-    //    discard;
-	//vec2 testColor = parallaxMappingNew(texCoord, eye);
-	
 	// Get diffuse color
 	vec4 diffuse = texture(diffuseTexture, newCoords).rgba;
 	
@@ -366,7 +350,6 @@ void main(void)
 	float roughness = getRoughness(newCoords);
 	float metalness = getMetalness(newCoords);
 	vec4 emissiveColor = texture(emissiveTexture, newCoords).rgba;
-	//vec4 emissiveColor = pow(texture(emissiveTexture, newCoords), vec4(gamma, gamma, gamma, 1.0));
 	
 	// Use emissive alpha channel as an intensity multiplier
 	emissiveColor *= emissiveMultiplier;

+ 2 - 1
Praxis3D/Data/Shaders/tonemapping.frag

@@ -4,6 +4,7 @@ out vec4 outputColor;
 
 uniform ivec2 screenSize;
 uniform float gamma;
+uniform float luminanceMultiplier;
 uniform int tonemapMethod;
 
 uniform sampler2D inputColorMap;
@@ -250,7 +251,7 @@ void main(void)
 	vec3 fragmentColor = texture2D(inputColorMap, texCoord).xyz;
 
 	// Get the average scene luminance
-	float luminance = texture2D(averageLuminanceTexture, vec2(0.0, 0.0)).r;
+	float luminance = texture2D(averageLuminanceTexture, vec2(0.0, 0.0)).r * luminanceMultiplier;
 	
 	// Perform exposure compensation by converting the color from RGB to Yxy color space and adjusting the luminosity component
 	vec3 colorYxy = convertRGB2Yxy(fragmentColor);

+ 1 - 1
Praxis3D/Data/config.ini

@@ -20,7 +20,7 @@ mouse_captured 1
 mouse_warp_mode 0
 fov 80
 z_near 0.1
-z_far 4000
+z_far 40000
 default_map componentTest.pmap
 atm_scattering_ground_frag_shader atmosphericScatteringPass_ground_simple.frag
 lens_flare_ghost_threshold 2.0

+ 69 - 25
Praxis3D/Source/AudioScene.cpp

@@ -27,7 +27,7 @@ AudioScene::AudioScene(SystemBase *p_system, SceneLoader *p_sceneLoader) : Syste
 
 AudioScene::~AudioScene()
 {
-	deactivate();
+	//deactivate();
 }
 
 ErrorCode AudioScene::init()
@@ -563,10 +563,72 @@ SystemObject *AudioScene::createComponent(const EntityID &p_entityID, const Soun
 	return returnObject;
 }
 
+void AudioScene::releaseObject(SystemObject *p_systemObject)
+{
+	switch(p_systemObject->getObjectType())
+	{
+		case Properties::PropertyID::SoundComponent:
+			{
+				auto *component = static_cast<SoundComponent *>(p_systemObject);
+
+				// If component is active and sound is playing, stop the sound
+				if(component->isObjectActive())
+				{
+					if(component->m_playing)
+					{
+						component->m_channel->stop();
+						component->m_playing = false;
+					}
+				}
+
+				// If sound exists, release its memory
+				if(component->m_sound != nullptr)
+					component->m_sound->release();
+			}
+			break;
+
+		case Properties::PropertyID::SoundListenerComponent:
+			{
+				auto *component = static_cast<SoundListenerComponent *>(p_systemObject);
+
+				// Nothing to release
+			}
+			break;
+	}
+}
+
 ErrorCode AudioScene::destroyObject(SystemObject *p_systemObject)
 {
-	// If this point is reached, no object was found, return an appropriate error
-	return ErrorCode::Destroy_obj_not_found;
+	ErrorCode returnError = ErrorCode::Success;
+
+	// Get the world scene required for deleting components
+	WorldScene *worldScene = static_cast<WorldScene *>(m_sceneLoader->getSystemScene(Systems::World));
+
+	switch(p_systemObject->getObjectType())
+	{
+		case Properties::PropertyID::SoundComponent:
+			{
+				// Delete component
+				worldScene->removeComponent<SoundComponent>(p_systemObject->getEntityID());
+			}
+			break;
+
+		case Properties::PropertyID::SoundListenerComponent:
+			{
+				// Delete component
+				worldScene->removeComponent<SoundListenerComponent>(p_systemObject->getEntityID());
+			}
+			break;
+
+		default:
+			{
+				// If this point is reached, no object was found, return an appropriate error
+				returnError = ErrorCode::Destroy_obj_not_found;
+			}
+			break;
+	}
+
+	return returnError;
 }
 
 void AudioScene::changeOccurred(ObservedSubject *p_subject, BitMask p_changeType)
@@ -631,24 +693,8 @@ void AudioScene::receiveData(const DataType p_dataType, void *p_data, const bool
 							// Check if the component exists
 							auto *component = entityRegistry.try_get<SoundComponent>(componentData->m_entityID);
 							if(component != nullptr)
-							{
-								// If component is active and sound is playing, stop the sound
-								if(component->isObjectActive())
-								{
-									if(component->m_playing)
-									{
-										component->m_channel->stop();
-										component->m_playing = false;
-									}
-								}
-
-								// If sound exists, release its memory
-								if(component->m_sound != nullptr)
-									component->m_sound->release();
-
-								// Delete component
-								worldScene->removeComponent<SoundComponent>(componentData->m_entityID);
-							}
+								if(auto error = destroyObject(component); error != ErrorCode::Success)
+									ErrHandlerLoc::get().log(error, component->getName(), ErrorSource::Source_AudioScene);
 						}
 						break;
 
@@ -657,10 +703,8 @@ void AudioScene::receiveData(const DataType p_dataType, void *p_data, const bool
 							// Check if the component exists
 							auto *component = entityRegistry.try_get<SoundListenerComponent>(componentData->m_entityID);
 							if(component != nullptr)
-							{
-								// Delete component
-								worldScene->removeComponent<SoundListenerComponent>(componentData->m_entityID);
-							}
+								if(auto error = destroyObject(component); error != ErrorCode::Success)
+									ErrHandlerLoc::get().log(error, component->getName(), ErrorSource::Source_AudioScene);
 						}
 						break;
 				}

+ 2 - 0
Praxis3D/Source/AudioScene.h

@@ -114,6 +114,8 @@ public:
 		p_constructionInfo.m_listenerID = p_component.getListenerID();
 	}
 
+	void releaseObject(SystemObject *p_systemObject);
+
 	ErrorCode destroyObject(SystemObject *p_systemObject);
 
 	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType);

+ 3 - 0
Praxis3D/Source/AudioSystem.h

@@ -93,6 +93,9 @@ public:
 	{
 		if(m_audioScenes[p_engineState] != nullptr)
 		{
+			// Shutdown the scene before destroying it
+			m_audioScenes[p_engineState]->shutdown();
+
 			delete m_audioScenes[p_engineState];
 			m_audioScenes[p_engineState] = nullptr;
 		}

+ 71 - 79
Praxis3D/Source/ChangeController.cpp

@@ -174,53 +174,71 @@ ErrorCode ChangeController::unregisterSubject(ObservedSubject *p_subject, Observ
 	return returnError;
 }
 
-ErrorCode ChangeController::distributeChanges(BitMask p_systemsToNotify, BitMask p_changesToDistribute)
+ErrorCode ChangeController::removeSubject(ObservedSubject *p_subject)
 {
-	// Store the parameters so they can be used by multiple threads
-	m_systemsToNotify = p_systemsToNotify;
-	m_changesToDistribute = p_changesToDistribute;
+	// TODO ERRORS
+	ErrorCode returnError = ErrorCode::Failure;
 
-	// Loop through the notifications.
-	// Some of them might generate more notifications, so it might need to loop through multiple times
-	while(true)
+	std::vector<ObserverRequest> observerList;
 	{
-		// Iterate over every thread-specific one-time data list
-		for(decltype(m_oneTimeDataLists)::iterator listIterator = m_oneTimeDataLists.begin(); listIterator != m_oneTimeDataLists.end(); listIterator++)
-		{
-			// Get the list of a single thread
-			auto &currentList = **listIterator;
+		SpinWait::Lock lock(m_spinWaitUpdate);
 
-			// Loop over ever notification in this list
-			for(decltype(currentList.size()) i = 0; i < currentList.size(); i++)
+		const auto ID = p_subject->getID(this);
+		//_ASSERT(ID != unsigned int(-1));
+		//_ASSERT(m_subjectsList[ID].m_subject == p_subject);
+		if(ID != unsigned int(-1))
+		{
+			if(m_subjectsList.size() <= ID || m_subjectsList[ID].m_subject != p_subject)
 			{
-				// Notify the observer about the change. We cannot check if the change is desired, since the
-				// observer is not registered with the change controller in one-time changes.
-				currentList[i].m_observer->receiveData(currentList[i].m_dataType, currentList[i].m_data, currentList[i].m_deleteAfterReceiving);
+				return ErrorCode::Failure;
+				// TODO ERROR FAILURE
 			}
-
-			// Clear out the list before moving to the next one
-			currentList.clear();
+			observerList = m_subjectsList[ID].m_observersList;
+			m_subjectsList[ID].m_subject = NULL;
+			m_freeIDsList.push_back(ID);
+			returnError = ErrorCode::Success;
+			// TODO ERROR SUCCESS
 		}
+	}
 
-		// Iterate over every thread-specific one-time notification list
-		for(decltype(m_oneTimeNotifyLists)::iterator listIterator = m_oneTimeNotifyLists.begin(); listIterator != m_oneTimeNotifyLists.end(); listIterator++)
-		{
-			// Get the list of a single thread
-			auto &currentList = **listIterator;
+	std::vector<ObserverRequest>::iterator listIterator = observerList.begin();
+	for(; listIterator != observerList.end(); listIterator++)
+	{
+		p_subject->detach(listIterator->m_observer);
+	}
 
-			// BUGFIX: compare the iterator to container size during every loop (instead of assigning the size to a variable once at start), 
-			// as observers upon receiving data might send out One Time notifications themselves, and the container size would increase during the iteration of the container
-			for(decltype(currentList.size()) i = 0; i < currentList.size(); i++)
-			{
-				// Notify the observer about the change. We cannot check if the change is desired, since the
-				// observer is not registered with the change controller in one-time changes.
-				currentList[i].m_observer->changeOccurred(currentList[i].m_subject, currentList[i].m_changedBits);
-			}
+	return returnError;
+}
 
-			// Clear out the list before moving to the next one
-			currentList.clear();
+ErrorCode ChangeController::distributeChanges(BitMask p_systemsToNotify, BitMask p_changesToDistribute)
+{
+	// Store the parameters so they can be used by multiple threads
+	m_systemsToNotify = p_systemsToNotify;
+	m_changesToDistribute = p_changesToDistribute;
+
+	// Iterate over every thread-specific one-time notification list
+	for(decltype(m_oneTimeNotifyLists)::iterator listIterator = m_oneTimeNotifyLists.begin(); listIterator != m_oneTimeNotifyLists.end(); listIterator++)
+	{
+		// Get the list of a single thread
+		auto &currentList = **listIterator;
+
+		// BUGFIX: compare the iterator to container size during every loop (instead of assigning the size to a variable once at start), 
+		// as observers upon receiving data might send out One Time notifications themselves, and the container size would increase during the iteration of the container
+		for(decltype(currentList.size()) i = 0; i < currentList.size(); i++)
+		{
+			// Notify the observer about the change. We cannot check if the change is desired, since the
+			// observer is not registered with the change controller in one-time changes.
+			currentList[i].m_observer->changeOccurred(currentList[i].m_subject, currentList[i].m_changedBits);
 		}
 
+		// Clear out the list before moving to the next one
+		currentList.clear();
+	}
+
+	// Loop through the notifications.
+	// Some of them might generate more notifications, so it might need to loop through multiple times
+	while(true)
+	{
 		// Make sure index list is big enough to contain all subjects
 		m_indexList.resize(m_subjectsList.size());
 
@@ -263,8 +281,6 @@ ErrorCode ChangeController::distributeChanges(BitMask p_systemsToNotify, BitMask
 				}
 			}
 
-			//std::cout << currentList->size() << "; ";
-
 			// Clear out the list before moving to the next one
 			currentList->clear();
 		}
@@ -302,6 +318,24 @@ ErrorCode ChangeController::distributeChanges(BitMask p_systemsToNotify, BitMask
 		}
 	}
 
+	// Iterate over every thread-specific one-time data list
+	for(decltype(m_oneTimeDataLists)::iterator listIterator = m_oneTimeDataLists.begin(); listIterator != m_oneTimeDataLists.end(); listIterator++)
+	{
+		// Get the list of a single thread
+		auto &currentList = **listIterator;
+
+		// Loop over ever notification in this list
+		for(decltype(currentList.size()) i = 0; i < currentList.size(); i++)
+		{
+			// Notify the observer about the change. We cannot check if the change is desired, since the
+			// observer is not registered with the change controller in one-time changes.
+			currentList[i].m_observer->receiveData(currentList[i].m_dataType, currentList[i].m_data, currentList[i].m_deleteAfterReceiving);
+		}
+
+		// Clear out the list before moving to the next one
+		currentList.clear();
+	}
+
 	return ErrorCode::Success;
 }
 void ChangeController::changeOccurred(ObservedSubject *p_subject, BitMask p_changedBits)
@@ -327,16 +361,10 @@ void ChangeController::changeOccurred(ObservedSubject *p_subject, BitMask p_chan
 			// Get thread local notification list
 			auto *notifyList = getNotifyList(m_tlsNotifyList);
 
-			//std::cout << "test ( ";
-
-			//std::cout << m_tlsNotifyList << " ";
-
 			if(notifyList != nullptr)
 			{
 				// Don't check for duplicates, for performance reasons
 				notifyList->push_back(Notification(p_subject, p_changedBits));
-
-				//std::cout << "size: " << notifyList->size() << " ";
 			}
 			else
 			{
@@ -345,8 +373,6 @@ void ChangeController::changeOccurred(ObservedSubject *p_subject, BitMask p_chan
 			}
 		}
 	}
-
-	//std::cout << ") test" << std::endl;
 }
 
 void ChangeController::oneTimeChange(ObservedSubject *p_subject, Observer *p_observer, BitMask p_changedBits)
@@ -546,40 +572,6 @@ void ChangeController::distributeRange(unsigned int p_begin, unsigned int p_end)
 	}
 }
 
-ErrorCode ChangeController::removeSubject(ObservedSubject *p_subject)
-{
-	// TODO ERRORS
-	ErrorCode returnError = ErrorCode::Failure;
-
-	std::vector<ObserverRequest> observerList;
-	{
-		SpinWait::Lock lock(m_spinWaitUpdate);
-
-		const auto ID = p_subject->getID(this);
-		_ASSERT(ID != unsigned int(-1));
-		_ASSERT(m_subjectsList[ID].m_subject == p_subject);
-
-		if(m_subjectsList.size() <= ID || m_subjectsList[ID].m_subject != p_subject)
-		{
-			return ErrorCode::Failure;
-			// TODO ERROR FAILURE
-		}
-		observerList = m_subjectsList[ID].m_observersList;
-		m_subjectsList[ID].m_subject = NULL;
-		m_freeIDsList.push_back(ID);
-		returnError = ErrorCode::Success;
-		// TODO ERROR SUCCESS
-	}
-
-	std::vector<ObserverRequest>::iterator listIterator = observerList.begin();
-	for(; listIterator != observerList.end(); listIterator++)
-	{
-		p_subject->detach(listIterator->m_observer);
-	}
-
-	return returnError;
-}
-
 inline std::vector<ChangeController::Notification> *ChangeController::getNotifyList(unsigned int p_tlsIndex)
 {
 	return static_cast<std::vector<ChangeController::Notification>*>(::TlsGetValue(p_tlsIndex));

+ 2 - 2
Praxis3D/Source/ChangeController.h

@@ -24,6 +24,8 @@ public:
 	ErrorCode registerSubject(ObservedSubject *p_subject, BitMask p_interestedBits, Observer *p_observer, BitMask p_observerBits = Systems::Types::All);
 	ErrorCode unregisterSubject(ObservedSubject *p_subject, Observer *p_observer);
 
+	ErrorCode removeSubject(ObservedSubject *p_subject);
+
 	ErrorCode distributeChanges(BitMask p_systemsToNotify = Systems::Types::All, BitMask p_changesToDistribute = Systems::Changes::All);
 	void changeOccurred(ObservedSubject *p_subject, BitMask p_changeType);
 
@@ -125,8 +127,6 @@ private:
 
 	void distributeRange(unsigned int p_begin, unsigned int p_end);
 
-	ErrorCode removeSubject(ObservedSubject *p_subject);
-
 	std::vector<Notification> *getNotifyList(unsigned int p_tlsIndex);
 	std::vector<OneTimeNotification> *getOneTimeNotifyList(unsigned int p_tlsIndex);
 	std::vector<OneTimeData> *getOneTimeDataList(unsigned int p_tlsIndex);

+ 2 - 1
Praxis3D/Source/CommonDefinitions.h

@@ -29,7 +29,8 @@ constexpr EntityID NULL_ENTITY_ID = std::numeric_limits<EntityID>::max();
 	/* World components */ \
 	Code(ComponentType_ObjectMaterialComponent, ) \
 	Code(ComponentType_SpatialComponent, ) \
-	Code(ComponentType_NumOfTypes, )
+	Code(ComponentType_NumOfTypes, ) \
+	Code(ComponentType_Entity, )
 DECLARE_ENUM(ComponentType, COMPONENT_TYPE)
 
 enum EngineChangeType : unsigned int

+ 4 - 1
Praxis3D/Source/Config.cpp

@@ -187,7 +187,8 @@ void Config::init()
 	AddVariablePredef(m_graphicsVar, light_color_r);
 	AddVariablePredef(m_graphicsVar, light_color_g);
 	AddVariablePredef(m_graphicsVar, light_color_b);
-	AddVariablePredef(m_graphicsVar, LOD_prallax_mapping);
+	AddVariablePredef(m_graphicsVar, LOD_parallax_mapping);
+	AddVariablePredef(m_graphicsVar, luminance_multiplier);
 	AddVariablePredef(m_graphicsVar, luminance_range_min);
 	AddVariablePredef(m_graphicsVar, luminance_range_max);
 	AddVariablePredef(m_graphicsVar, height_scale);
@@ -229,6 +230,7 @@ void Config::init()
 	AddVariablePredef(m_GUIVar, editor_button_reload_texture);
 	AddVariablePredef(m_GUIVar, editor_button_restart_texture);
 	AddVariablePredef(m_GUIVar, editor_button_scripting_enabled_texture);
+	AddVariablePredef(m_GUIVar, editor_new_entity_name);
 	AddVariablePredef(m_GUIVar, gui_editor_window_name);
 
 	// Input variables
@@ -458,6 +460,7 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, bloomDirtIntensity);
 	AddVariablePredef(m_shaderVar, inverseLogLuminanceRange);
 	AddVariablePredef(m_shaderVar, logLuminanceRange);
+	AddVariablePredef(m_shaderVar, luminanceMultiplier);
 	AddVariablePredef(m_shaderVar, minLogLuminance);
 	AddVariablePredef(m_shaderVar, tonemapMethod);
 	AddVariablePredef(m_shaderVar, lensFlareDirtTextureUniform);

+ 12 - 5
Praxis3D/Source/Config.h

@@ -248,12 +248,12 @@ namespace Systems
 			static constexpr BitMask UpVector				= Changes::Type::Graphics + Changes::Graphics::Camera + Changes::Common::Shared2;
 			static constexpr BitMask AllCamera				= Target | UpVector;
 
-			static constexpr BitMask LightEnabled			= Changes::Type::Graphics + Changes::Graphics::Lighting + Changes::Common::Shared3;
+			static constexpr BitMask LightType				= Changes::Type::Graphics + Changes::Graphics::Lighting + Changes::Common::Shared3;
 			static constexpr BitMask Color					= Changes::Type::Graphics + Changes::Graphics::Lighting + Changes::Common::Shared4;
 			static constexpr BitMask CutoffAngle			= Changes::Type::Graphics + Changes::Graphics::Lighting + Changes::Common::Shared5;
 			static constexpr BitMask Direction				= Changes::Type::Graphics + Changes::Graphics::Lighting + Changes::Common::Shared6;
 			static constexpr BitMask Intensity				= Changes::Type::Graphics + Changes::Graphics::Lighting + Changes::Common::Shared7;
-			static constexpr BitMask AllLighting			= LightEnabled | Color | CutoffAngle | Direction | Intensity;
+			static constexpr BitMask AllLighting			= LightType | Color | CutoffAngle | Direction | Intensity;
 
 			static constexpr BitMask PositionBuffer			= Changes::Type::Graphics + Changes::Graphics::Framebuffers + Changes::Common::Shared8;
 			static constexpr BitMask DiffuseBuffer			= Changes::Type::Graphics + Changes::Graphics::Framebuffers + Changes::Common::Shared9;
@@ -295,7 +295,8 @@ namespace Systems
 		namespace World
 		{
 			static constexpr BitMask ObjectMaterialType		= Changes::Type::World + Changes::Common::Shared1;
-			static constexpr BitMask All					= ObjectMaterialType;
+			static constexpr BitMask PrefabName				= Changes::Type::World + Changes::Common::Shared2;
+			static constexpr BitMask All					= ObjectMaterialType | PrefabName;
 		}
 
 		static constexpr BitMask None = 0;
@@ -886,7 +887,8 @@ public:
 			light_color_r = 1.0f;
 			light_color_g = 1.0f;
 			light_color_b = 1.0f;
-			LOD_prallax_mapping = 100.0f;
+			LOD_parallax_mapping = 100.0f;
+			luminance_multiplier = 0.5f;
 			luminance_range_min = 0.004f;
 			luminance_range_max = 11.3f;
 			height_scale = 0.0f;
@@ -947,7 +949,8 @@ public:
 		float light_color_r;
 		float light_color_g;
 		float light_color_b;
-		float LOD_prallax_mapping;
+		float LOD_parallax_mapping;
+		float luminance_multiplier;
 		float luminance_range_min;
 		float luminance_range_max;
 		float height_scale;
@@ -992,6 +995,7 @@ public:
 			editor_button_reload_texture = "buttons\\button_reload_3.png";
 			editor_button_restart_texture = "buttons\\button_editor_restart_2.png";
 			editor_button_scripting_enabled_texture = "buttons\\button_scripting_3.png";
+			editor_new_entity_name = "New Entity";
 			gui_editor_window_name = "Editor window";
 		}
 		bool gui_docking_enabled;
@@ -1027,6 +1031,7 @@ public:
 		std::string editor_button_reload_texture;
 		std::string editor_button_restart_texture;
 		std::string editor_button_scripting_enabled_texture;
+		std::string editor_new_entity_name;
 		std::string gui_editor_window_name;
 	};
 	struct InputVariables
@@ -1438,6 +1443,7 @@ public:
 
 			inverseLogLuminanceRange = "inverseLogLuminanceRange";
 			logLuminanceRange = "logLuminanceRange";
+			luminanceMultiplier = "luminanceMultiplier";
 			minLogLuminance = "minLogLuminance";
 			tonemapMethod = "tonemapMethod";
 
@@ -1541,6 +1547,7 @@ public:
 
 		std::string inverseLogLuminanceRange;
 		std::string logLuminanceRange;
+		std::string luminanceMultiplier;
 		std::string minLogLuminance;
 		std::string tonemapMethod;
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1165 - 1076
Praxis3D/Source/EditorWindow.cpp


+ 67 - 1
Praxis3D/Source/EditorWindow.h

@@ -28,9 +28,20 @@ public:
 		m_translateGuizmoEnabled = true;
 		m_rotateGuizmoEnabled = false;
 		m_showNewMapWindow = false;
+		m_activatedMainMenuButton = MainMenuButtonType::MainMenuButtonType_None;
 		m_sceneState = EditorSceneState::EditorSceneState_Pause;
 		m_centerWindowSize = glm::ivec2(0);
 
+		m_keys[KeyType::KeyType_Ctlr].bind(Scancode::Key_leftctrl);
+		m_keys[KeyType::KeyType_Shift].bind(Scancode::Key_leftshift);
+		m_keys[KeyType::KeyType_Alt].bind(Scancode::Key_leftalt);
+		m_keys[KeyType::KeyType_N].bind(Scancode::Key_N);
+		m_keys[KeyType::KeyType_O].bind(Scancode::Key_O);
+		m_keys[KeyType::KeyType_S].bind(Scancode::Key_S);
+		m_keys[KeyType::KeyType_R].bind(Scancode::Key_R);
+		m_keys[KeyType::KeyType_Esc].bind(Scancode::Key_esc);
+		m_keys[KeyType::KeyType_F4].bind(Scancode::Key_F4);
+
 		m_selectedTexture = nullptr;
 		m_textureInspectorTabFlags = 0;
 
@@ -51,6 +62,11 @@ public:
 		for(unsigned int i = 0; i < RenderPassType::RenderPassType_NumOfTypes; i++)
 			m_renderingPassesTypeText.push_back(GetString(static_cast<RenderPassType>(i)));
 
+		m_tonemappingMethodText = { "None", "Simple reinhard", "Reinhard with white point", "Filmic tonemapping", "Uncharted 2", "Unreal 3", "ACES", "Lottes", "Uchimura" };
+
+		m_nextEntityToSelect = NULL_ENTITY_ID;
+		m_pendingEntityToSelect = false;
+
 		m_newEntityConstructionInfo = nullptr;
 		m_openNewEntityPopup = false;
 
@@ -63,6 +79,8 @@ public:
 
 		m_buttonBackgroundEnabled = ImVec4(0.26f, 0.26f, 0.26f, 1.0f);
 		m_buttonBackgroundDisabled = ImVec4(0.0f, 0.0f, 0.0f, 0.0f);
+		m_mousePositionOnNewEntity = ImVec2(0.0f, 0.0f);
+		m_newEntityWindowInitialized = false;
 	}
 	~EditorWindow();
 
@@ -257,6 +275,13 @@ public:
 						return (unsigned int)m_selectedEntity.m_objectMaterialType;
 				}
 				break;
+
+			case Systems::Changes::Graphics::LightType:
+				{
+					if(m_selectedEntity.m_lightType >= 0 && m_selectedEntity.m_lightType < LightComponent::LightComponentType::LightComponentType_spot + 1)
+						return (unsigned int)m_selectedEntity.m_lightType;
+				}
+				break;
 		}
 
 		return NullObjects::NullUnsignedInt;
@@ -357,6 +382,12 @@ public:
 					return m_selectedEntity.m_componentData.m_scriptComponents.m_luaConstructionInfo->m_luaScriptFilename;
 				}
 				break;
+
+			case Systems::Changes::World::PrefabName:
+				{
+					return m_selectedEntity.m_componentData.m_prefab;
+				}
+				break;
 		}
 
 		return NullObjects::NullString; 
@@ -516,6 +547,7 @@ private:
 			m_luaVariablesModified = false;
 			m_luaScriptFilenameModified = false;
 			m_modelDataModified = false;
+			m_prefabNameModified = false;
 			m_soundFilenameModified = false;
 			m_selectedModelName = nullptr;
 			m_selectedTextureName = nullptr;
@@ -574,6 +606,7 @@ private:
 		bool m_luaVariablesModified;
 		bool m_luaScriptFilenameModified;
 		bool m_modelDataModified;
+		bool m_prefabNameModified;
 		bool m_soundFilenameModified;
 	};
 	struct SceneData
@@ -637,7 +670,32 @@ private:
 		FileBrowserActivated_SoundFile,
 		FileBrowserActivated_ModelFile,
 		FileBrowserActivated_TextureFile,
-		FileBrowserActivated_AudioBankFile
+		FileBrowserActivated_AudioBankFile,
+		FileBrowserActivated_PrefabFile
+	};
+	enum MainMenuButtonType : unsigned int
+	{
+		MainMenuButtonType_None = 0,
+		MainMenuButtonType_New,
+		MainMenuButtonType_Open,
+		MainMenuButtonType_Save,
+		MainMenuButtonType_SaveAs,
+		MainMenuButtonType_ReloadScene,
+		MainMenuButtonType_CloseEditor,
+		MainMenuButtonType_Exit
+	};
+	enum KeyType : unsigned int
+	{
+		KeyType_Ctlr,
+		KeyType_Shift,
+		KeyType_Alt,
+		KeyType_N,
+		KeyType_O,
+		KeyType_S,
+		KeyType_R,
+		KeyType_Esc,
+		KeyType_F4,
+		KeyType_NumOfKeys
 	};
 
 	void drawSceneData(SceneData &p_sceneData, const bool p_sendChanges = false);
@@ -732,6 +790,7 @@ private:
 			m_componentConstructionInfoPool.clear();
 		}
 	}
+	void processMainMenuButton(MainMenuButtonType &p_mainMenuButtonType);
 	void updateSceneData(SceneData &p_sceneData);
 	void updateEntityList();
 	void updateHierarchyList();
@@ -859,9 +918,11 @@ private:
 	bool m_translateGuizmoEnabled;
 	bool m_rotateGuizmoEnabled;
 	bool m_showNewMapWindow;
+	MainMenuButtonType m_activatedMainMenuButton;
 	EditorSceneState m_sceneState;
 	glm::ivec2 m_centerWindowSize;
 	std::vector<const char *> m_physicalMaterialProperties;
+	KeyCommand m_keys[KeyType::KeyType_NumOfKeys];
 
 	// GUI settings
 	ImGuiColorEditFlags m_colorEditFlags;
@@ -873,6 +934,8 @@ private:
 	ImVec2 m_textureAssetImageSize;
 	ImVec4 m_buttonBackgroundEnabled;
 	ImVec4 m_buttonBackgroundDisabled;
+	ImVec2 m_mousePositionOnNewEntity;
+	bool m_newEntityWindowInitialized;
 
 	// LUA variables editor data
 	std::vector<const char *> m_luaVariableTypeStrings;
@@ -899,6 +962,8 @@ private:
 	EntityHierarchyEntry m_rootEntityHierarchyEntry;
 	SelectedEntity m_selectedEntity;
 	SceneData m_currentSceneData;
+	EntityID m_nextEntityToSelect;
+	bool m_pendingEntityToSelect;
 
 	// Used to hold entity and component data for component creation / deletion until the next frame, after sending the data as a change
 	std::vector<EntityAndComponent*> m_entityAndComponentPool;
@@ -910,6 +975,7 @@ private:
 	SceneData m_newSceneData;
 	ImGuiTabItemFlags m_newSceneSettingsTabFlags;
 	std::vector<const char *> m_renderingPassesTypeText;
+	std::vector<const char *> m_tonemappingMethodText;
 
 	// Button textures
 	std::vector<TextureLoader2D::Texture2DHandle> m_buttonTextures;

+ 4 - 1
Praxis3D/Source/Engine.cpp

@@ -167,9 +167,12 @@ void Engine::processEngineChanges()
 					break;
 				case EngineChangeType_SceneReload:
 					{
+						std::string filename = change.m_filename;
+
 						// Delete the current scene
 						if(m_engineStates[change.m_engineStateType] != nullptr)
 						{
+							filename = m_engineStates[change.m_engineStateType]->getSceneFilename();
 							delete m_engineStates[change.m_engineStateType];
 							m_engineStates[change.m_engineStateType] = nullptr;
 						}
@@ -181,7 +184,7 @@ void Engine::processEngineChanges()
 							if(change.m_sceneProperties)
 								stateLoaded = loadState(change.m_engineStateType, change.m_sceneProperties);
 							else
-								stateLoaded = loadState(change.m_engineStateType, change.m_filename);
+								stateLoaded = loadState(change.m_engineStateType, filename);
 
 							if(stateLoaded)
 								setCurrentState(change.m_engineStateType);

+ 17 - 9
Praxis3D/Source/EngineState.cpp

@@ -103,6 +103,23 @@ void EngineState::shutdown()
 		//m_objectChangeController->resetTaskManager();
 		//m_sceneChangeController->resetTaskManager();
 
+		// Get all engine systems
+		auto systems = m_engine.getSystems();
+
+		// Delete the World scene first, as it contains the entity registry, and components need their scene to still be available when destructing
+		m_changeCtrlScene->unextend(systems[Systems::World]->getScene(m_engineStateType));
+		systems[Systems::World]->deleteScene(m_engineStateType);
+
+		// Delete all system scenes that belong to this engine state
+		for(int i = 0; i < Systems::NumberOfSystems; i++)
+		{
+			if(i != Systems::World)
+			{
+				m_changeCtrlScene->unextend(systems[i]->getScene(m_engineStateType));
+				systems[i]->deleteScene(m_engineStateType);
+			}
+		}
+
 		if(m_scheduler != nullptr)
 			delete m_scheduler;
 		if(m_changeCtrlScene != nullptr)
@@ -112,15 +129,6 @@ void EngineState::shutdown()
 		if(m_objectChangeController != nullptr)
 			delete m_objectChangeController;
 
-		// Get all engine systems
-		auto systems = m_engine.getSystems();
-
-		// Delete all system scenes that belong to this engine state
-		for(int i = 0; i < Systems::NumberOfSystems; i++)
-		{
-			systems[i]->deleteScene(m_engineStateType);
-		}
-
 		m_initialized = false;
 	}
 }

+ 2 - 0
Praxis3D/Source/EngineState.h

@@ -38,6 +38,8 @@ public:
 
 	const inline bool isInitialized() const { return m_initialized; }
 
+	const inline std::string &getSceneFilename() const { return m_sceneFilename; }
+
 	inline void setSceneFilename(const std::string &p_filename) { m_sceneFilename = p_filename; }
 
 protected:

+ 2 - 1
Praxis3D/Source/EntityViewDefinitions.h

@@ -13,7 +13,8 @@
 //class SpatialComponent;
 //class ShaderComponent;
 
-typedef entt::basic_view<unsigned int, entt::get_t<ModelComponent, SpatialComponent>, entt::exclude_t<ShaderComponent, GraphicsLoadToMemoryComponent, GraphicsLoadToVideoMemoryComponent>> ModelSpatialView;
+//typedef entt::basic_view<unsigned int, entt::get_t<ModelComponent, SpatialComponent>, entt::exclude_t<ShaderComponent, GraphicsLoadToMemoryComponent, GraphicsLoadToVideoMemoryComponent>> ModelSpatialView;
+//typedef entt::basic_view<entt::get_t<entt::basic_sigh_mixin<entt::basic_storage<Type, Entity, Allocator, void>, entt::basic_registry<Entity, std::allocator<std::seed_seq::result_type>>>, entt::basic_sigh_mixin<entt::basic_storage<SpatialComponent, Entity, std::allocator<SpatialComponent>, void>, entt::basic_registry<Entity, std::allocator<std::seed_seq::result_type>>>>, entt::exclude_t<entt::basic_sigh_mixin<entt::basic_storage<ShaderComponent, Entity, std::allocator<ShaderComponent>, void>, entt::basic_registry<Entity, std::allocator<std::seed_seq::result_type>>>, entt::basic_sigh_mixin<entt::basic_storage<GraphicsLoadToMemoryComponent, Entity, std::allocator<GraphicsLoadToMemoryComponent>, void>, entt::basic_registry<Entity, std::allocator<std::seed_seq::result_type>>>, entt::basic_sigh_mixin<entt::basic_storage<GraphicsLoadToVideoMemoryComponent, Entity, std::allocator<GraphicsLoadToVideoMemoryComponent>, void>, entt::basic_registry<Entity, std::allocator<std::seed_seq::result_type>>>>, void> ModelSpatialView;
 typedef entt::basic_view<unsigned int, entt::get_t<ModelComponent, ShaderComponent, SpatialComponent>, entt::exclude_t<GraphicsLoadToMemoryComponent, GraphicsLoadToVideoMemoryComponent>> ModelShaderSpatialView;
 typedef entt::basic_view<unsigned int, entt::get_t<LightComponent, SpatialComponent>, entt::exclude_t<>> LightSpatialView;
 

+ 31 - 11
Praxis3D/Source/GUIScene.cpp

@@ -271,20 +271,41 @@ SystemObject *GUIScene::createComponent(const EntityID p_entityID, const GUISequ
 	return returnObject;
 }
 
+void GUIScene::releaseObject(SystemObject *p_systemObject)
+{
+	switch(p_systemObject->getObjectType())
+	{
+		case Properties::PropertyID::GUISequenceComponent:
+			{
+				auto *component = static_cast<GUISequenceComponent *>(p_systemObject);
+
+				// Nothing to release
+			}
+			break;
+	}
+}
+
 ErrorCode GUIScene::destroyObject(SystemObject *p_systemObject)
 {	
 	ErrorCode returnError = ErrorCode::Success;
 
+	// Get the world scene required for deleting components
+	WorldScene *worldScene = static_cast<WorldScene *>(m_sceneLoader->getSystemScene(Systems::World));
+
 	switch(p_systemObject->getObjectType())
 	{
 	case Properties::PropertyID::GUISequenceComponent:
-		//m_sceneLoader->getChangeController()->removeObjectLink(p_systemObject);
-		static_cast<WorldScene *>(m_sceneLoader->getSystemScene(Systems::World))->removeComponent<GUISequenceComponent>(p_systemObject->getEntityID());
+		{
+			// Delete component
+			worldScene->removeComponent<GUISequenceComponent>(p_systemObject->getEntityID());
+		}
 		break;
 
 	default:
-		// No object was found, return an appropriate error
-		returnError = ErrorCode::Destroy_obj_not_found;
+		{
+			// No object was found, return an appropriate error
+			returnError = ErrorCode::Destroy_obj_not_found;
+		}
 		break;
 	}
 
@@ -315,10 +336,8 @@ void GUIScene::receiveData(const DataType p_dataType, void *p_data, const bool p
 							// Check if the component exists
 							auto *component = entityRegistry.try_get<GUISequenceComponent>(componentData->m_entityID);
 							if(component != nullptr)
-							{
-								// Delete component
-								worldScene->removeComponent<GUISequenceComponent>(componentData->m_entityID);
-							}
+								if(auto error = destroyObject(component); error != ErrorCode::Success)
+									ErrHandlerLoc::get().log(error, component->getName(), ErrorSource::Source_GUI);
 						}
 						break;
 				}
@@ -354,10 +373,11 @@ void GUIScene::receiveData(const DataType p_dataType, void *p_data, const bool p
 					{
 						m_editorWindow = new EditorWindow(this, Config::GUIVar().gui_editor_window_name, 0);
 						m_editorWindow->init();
-						m_editorWindow->setup(*editorWindowSettings);
 					}
-					else // If the editor window does exist, just send the settings to it
-						m_editorWindow->setup(*editorWindowSettings);
+
+					// Send the settings to the editor window
+					m_editorWindow->setup(*editorWindowSettings);
+					m_editorWindow->activate();
 
 					GUIHandlerLocator::get().enableDocking();
 				}

+ 2 - 0
Praxis3D/Source/GUIScene.h

@@ -155,6 +155,8 @@ public:
 		p_constructionInfo.m_staticSequence = p_component.isStaticSequence();
 	}
 
+	void releaseObject(SystemObject *p_systemObject);
+
 	ErrorCode destroyObject(SystemObject *p_systemObject);
 
 	void changeOccurred(ObservedSubject* p_subject, BitMask p_changeType) { }

+ 3 - 0
Praxis3D/Source/GUISystem.h

@@ -74,6 +74,9 @@ public:
 	{
 		if(m_GUIScenes[p_engineState] != nullptr)
 		{
+			// Shutdown the scene before destroying it
+			m_GUIScenes[p_engineState]->shutdown();
+
 			delete m_GUIScenes[p_engineState];
 			m_GUIScenes[p_engineState] = nullptr;
 		}

+ 5 - 2
Praxis3D/Source/GraphicsDataSets.h

@@ -27,11 +27,12 @@ struct MaterialData
 // Contains data of a single mesh and its materials
 struct MeshData
 {
-	MeshData(const Model::Mesh &p_mesh, MaterialData p_materials[MaterialType::MaterialType_NumOfTypes], const float p_heightScale, const float p_alphaThreshold, const float p_emissiveIntensity) :
+	MeshData(const Model::Mesh &p_mesh, MaterialData p_materials[MaterialType::MaterialType_NumOfTypes], const float p_heightScale, const float p_alphaThreshold, const float p_emissiveIntensity, const bool p_active = true) :
 		m_mesh(&p_mesh), 
 		m_heightScale(p_heightScale),
 		m_alphaThreshold(p_alphaThreshold),
-		m_emissiveIntensity(p_emissiveIntensity)
+		m_emissiveIntensity(p_emissiveIntensity),
+		m_active(p_active)
 	{
 		std::copy(p_materials, p_materials + MaterialType::MaterialType_NumOfTypes, m_materials);
 	}
@@ -42,6 +43,8 @@ struct MeshData
 	float m_alphaThreshold;
 	// Multiplier for emissive texture color
 	float m_emissiveIntensity;
+	// Flag denoting whether to draw the mesh
+	bool m_active;
 
 	// Handle to a mesh
 	const Model::Mesh *m_mesh;

+ 59 - 17
Praxis3D/Source/LightComponent.h

@@ -128,17 +128,11 @@ public:
 
 	ErrorCode init()
 	{
-		// Mark the object as loaded, because there is nothing to be specifically loaded, at least for now
-		//setLoadedToMemory(true);
-		//setLoadedToVideoMemory(true);
-
 		return ErrorCode::Success;
 	}
 
 	void loadToMemory() 
 	{
-		//updatePosition(glm::vec3(m_spatialData->getWorldTransform()[3]));
-		//updateRotation(glm::vec3(m_spatialData->getWorldTransform()[2]));
 	}
 
 	// System type is Graphics
@@ -146,13 +140,6 @@ public:
 
 	void update(const float p_deltaTime)
 	{
-		// If the spatial data has changed, update the spatial data in light datasets
-		//if(hasSpatialDataUpdated())
-		//{
-		//	updatePosition(glm::vec3(m_spatialData->getWorldTransform()[3]));
-		//	updateRotation(glm::vec3(m_spatialData->getWorldTransform()[2]));
-		//}
-
 		if(isUpdateNeeded())
 		{
 			// Mark as updated
@@ -169,10 +156,7 @@ public:
 		if(CheckBitmask(p_changeType, Systems::Changes::Generic::Active))
 			setActive(p_subject->getBool(this, Systems::Changes::Generic::Active));
 
-		// Check if the light should be enabled/disabled
-		if(CheckBitmask(p_changeType, Systems::Changes::Graphics::LightEnabled))
-			m_active = p_subject->getBool(this, Systems::Changes::Graphics::LightEnabled);
-
+		// Check individual light data changes based on light type
 		switch(getLightType())
 		{
 			case LightComponent::LightComponentType_point:
@@ -214,6 +198,64 @@ public:
 			break;
 		}
 
+		// Check if the light type is changed
+		if(CheckBitmask(p_changeType, Systems::Changes::Graphics::LightType))
+		{
+			// Store the current light values
+			glm::vec3 color = glm::vec3(1.0f);
+			float intensity = 1.0f;
+			float cutoffAngle = 1.0f;
+
+			switch(getLightType())
+			{
+				case LightComponent::LightComponentType_point:
+					{
+						color = m_lightComponent.m_point.m_color;
+						intensity = m_lightComponent.m_point.m_intensity;
+					}
+					break;
+				case LightComponent::LightComponentType_spot:
+					{
+						color = m_lightComponent.m_spot.m_color;
+						intensity = m_lightComponent.m_spot.m_intensity;
+						cutoffAngle = m_lightComponent.m_spot.m_cutoffAngle;
+					}
+					break;
+				case LightComponent::LightComponentType_directional:
+					{
+						color = m_lightComponent.m_directional.m_color;
+						intensity = m_lightComponent.m_directional.m_intensity;
+					}
+					break;
+			}
+
+			// Get the new light type
+			auto newLightType = p_subject->getUnsignedInt(this, Systems::Changes::Graphics::LightType);
+
+			// Set the light data based on the new light type
+			switch(newLightType)
+			{
+				case LightComponent::LightComponentType_point:
+					{
+						m_lightComponent.m_point = PointLightDataSet(color, glm::vec3(0.0f), glm::vec3(0.0f, 0.0f, 1.0f), intensity);
+						m_lightComponentType = LightComponent::LightComponentType::LightComponentType_point;
+					}
+					break;
+				case LightComponent::LightComponentType_spot:
+					{
+						m_lightComponent.m_spot = SpotLightDataSet(color, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 0.0f), intensity, cutoffAngle);
+						m_lightComponentType = LightComponent::LightComponentType::LightComponentType_spot;
+					}
+					break;
+				case LightComponent::LightComponentType_directional:
+					{
+						m_lightComponent.m_directional = DirectionalLightDataSet(color, glm::vec3(0.0f, 1.0f, 0.0f), intensity);
+						m_lightComponentType = LightComponent::LightComponentType::LightComponentType_directional;
+					}
+					break;
+			}
+		}
+
 		// Remove the processed change mask from the bit mask value, and reset the processed change value
 		//p_changeType &= ~processedChange;
 		//processedChange = 0;

+ 1 - 0
Praxis3D/Source/LoaderBase.h

@@ -57,6 +57,7 @@ public:
 		inline const bool isLoadedToVideoMemory() const { return m_loadedToVideoMemory;		}
 		inline const unsigned int getUniqueID()	const	{ return (unsigned int)m_uniqueID;	}
 		inline const std::string &getFilename() const	{ return m_filename;				}
+		inline size_t getReferenceCounter() const		{ return m_refCounter;				}
 
 		// Equality operator; compares filenames
 		inline bool operator==(std::string p_string) { return m_filename == p_string; }

+ 1 - 1
Praxis3D/Source/LuaComponent.h

@@ -24,7 +24,7 @@ public:
 		std::vector<std::pair<std::string, Property>> m_variables;
 	};
 
-	LuaComponent(SystemScene *p_systemScene, std::string p_name, std::size_t p_id = 0) : SystemObject(p_systemScene, p_name, Properties::PropertyID::LuaComponent), m_luaSpatialData(*this), m_GUIData(*this)
+	LuaComponent(SystemScene *p_systemScene, const std::string &p_name, const EntityID p_entityID) : SystemObject(p_systemScene, p_name, Properties::PropertyID::LuaComponent, p_entityID), m_luaSpatialData(*this), m_GUIData(*this)
 	{
 		m_luaScript = new LuaScript(m_systemScene, m_luaSpatialData, m_GUIData);
 		m_luaScriptLoaded = false;

+ 5 - 0
Praxis3D/Source/MetadataComponent.h

@@ -53,6 +53,11 @@ public:
 		{
 			setName(p_subject->getString(this, Systems::Changes::Generic::Name));
 		}
+
+		if(CheckBitmask(p_changeType, Systems::Changes::World::PrefabName))
+		{
+			setPrefabName(p_subject->getString(this, Systems::Changes::World::PrefabName));
+		}
 	}
 
 private:

+ 14 - 2
Praxis3D/Source/ModelComponent.h

@@ -48,6 +48,10 @@ public:
 				if(p_size > m_heightScale.size())
 					m_heightScale.resize(p_size, Config::graphicsVar().height_scale);
 
+				// Resize the "mesh is active" array and initialize each element to true
+				if(p_size > m_active.size())
+					m_active.resize(p_size, true);
+
 				// Resize the "mesh is present" array and initialize each element to false
 				if(p_size > m_present.size())
 					m_present.resize(p_size, false);
@@ -63,6 +67,7 @@ public:
 			m_alphaThreshold.clear();
 			m_emissiveIntensity.clear();
 			m_heightScale.clear();
+			m_active.clear();
 			m_present.clear();
 		}
 
@@ -75,6 +80,7 @@ public:
 		std::vector<float> m_alphaThreshold;
 		std::vector<float> m_emissiveIntensity;
 		std::vector<float> m_heightScale;
+		std::vector<bool> m_active;
 		std::vector<bool> m_present;
 	};
 	struct ModelsProperties
@@ -272,6 +278,10 @@ public:
 								materials[iMatType].m_textureScale = glm::vec2(Config::graphicsVar().texture_tiling_factor, Config::graphicsVar().texture_tiling_factor);
 						}
 
+						bool active = true;
+						if(m_modelsProperties->m_models[modelIndex].m_active.size() > meshIndex)
+							active = m_modelsProperties->m_models[modelIndex].m_active[meshIndex];
+
 						float heightScale = Config::graphicsVar().height_scale;
 						if(m_modelsProperties->m_models[modelIndex].m_heightScale.size() > meshIndex)
 							heightScale = m_modelsProperties->m_models[modelIndex].m_heightScale[meshIndex];
@@ -290,7 +300,8 @@ public:
 							materials,
 							heightScale,
 							alphaThreshold,
-							emissiveIntensity));
+							emissiveIntensity,
+							active));
 					}
 					else
 					{
@@ -310,7 +321,7 @@ public:
 							materials[iMatType].m_textureScale = glm::vec2(Config::graphicsVar().texture_tiling_factor, Config::graphicsVar().texture_tiling_factor);
 						}
 
-						newModelData.m_meshes.push_back(MeshData(newModelData.m_model.getMeshArray()[meshIndex], materials, Config::graphicsVar().height_scale, Config::graphicsVar().alpha_threshold, Config::graphicsVar().emissive_multiplier));
+						newModelData.m_meshes.push_back(MeshData(newModelData.m_model.getMeshArray()[meshIndex], materials, Config::graphicsVar().height_scale, Config::graphicsVar().alpha_threshold, Config::graphicsVar().emissive_multiplier, true));
 
 						//ErrHandlerLoc::get().log(ErrorCode::Load_to_memory_success, ErrorSource::Source_ModelComponent, m_modelsProperties->m_models[modelIndex].m_modelName);
 					}
@@ -506,6 +517,7 @@ public:
 				newModelEntry.m_alphaThreshold.push_back(m_modelData[modelIndex].m_meshes[meshIndex].m_alphaThreshold);
 				newModelEntry.m_emissiveIntensity.push_back(m_modelData[modelIndex].m_meshes[meshIndex].m_emissiveIntensity);
 				newModelEntry.m_heightScale.push_back(m_modelData[modelIndex].m_meshes[meshIndex].m_heightScale);
+				newModelEntry.m_active.push_back(m_modelData[modelIndex].m_meshes[meshIndex].m_active);
 				newModelEntry.m_present.push_back(materialPresent);
 				newModelEntry.m_numOfMeshes++;
 			}

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio