瀏覽代碼

entt: constrain allocator types

skypjack 1 月之前
父節點
當前提交
3b71902777

+ 7 - 9
src/entt/container/dense_map.hpp

@@ -17,6 +17,7 @@
 #include "../config/config.h"
 #include "../core/bit.hpp"
 #include "../core/compressed_pair.hpp"
+#include "../core/concepts.hpp"
 #include "../core/iterator.hpp"
 #include "../core/memory.hpp"
 #include "../core/type_traits.hpp"
@@ -39,18 +40,16 @@ struct dense_map_node final {
         : next{pos},
           element{std::forward<Args>(args)...} {}
 
-    template<typename Allocator, typename... Args>
-    dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
+    template<typename... Args>
+    dense_map_node(std::allocator_arg_t, const allocator_like auto &allocator, const std::size_t pos, Args &&...args)
         : next{pos},
           element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
 
-    template<typename Allocator>
-    dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
+    dense_map_node(std::allocator_arg_t, const allocator_like auto &allocator, const dense_map_node &other)
         : next{other.next},
           element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
 
-    template<typename Allocator>
-    dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
+    dense_map_node(std::allocator_arg_t, const allocator_like auto &allocator, dense_map_node &&other)
         : next{other.next},
           element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
 
@@ -228,9 +227,8 @@ private:
  * @tparam Type Mapped type of the associative container.
  * @tparam Hash Type of function to use to hash the keys.
  * @tparam KeyEqual Type of function to use to compare the keys for equality.
- * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
+template<typename Key, typename Type, typename Hash, typename KeyEqual, allocator_like Allocator>
 class dense_map {
     static constexpr float default_threshold = 0.875f;
     static constexpr std::size_t minimum_capacity = 8u;
@@ -1020,7 +1018,7 @@ private:
 /*! @cond ENTT_INTERNAL */
 namespace std {
 
-template<typename Key, typename Value, typename Allocator>
+template<typename Key, typename Value, entt::allocator_like Allocator>
 struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
     : std::true_type {};
 

+ 1 - 2
src/entt/container/dense_set.hpp

@@ -190,9 +190,8 @@ private:
  * @tparam Type Value type of the associative container.
  * @tparam Hash Type of function to use to hash the values.
  * @tparam KeyEqual Type of function to use to compare the values for equality.
- * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
+template<typename Type, typename Hash, typename KeyEqual, allocator_like Allocator>
 class dense_set {
     static constexpr float default_threshold = 0.875f;
     static constexpr std::size_t minimum_capacity = 8u;

+ 3 - 2
src/entt/container/fwd.hpp

@@ -5,6 +5,7 @@
 #include <memory>
 #include <utility>
 #include <vector>
+#include "../core/concepts.hpp"
 
 namespace entt {
 
@@ -13,14 +14,14 @@ template<
     typename Type,
     typename = std::hash<Key>,
     typename = std::equal_to<>,
-    typename = std::allocator<std::pair<const Key, Type>>>
+    allocator_like = std::allocator<std::pair<const Key, Type>>>
 class dense_map;
 
 template<
     typename Type,
     typename = std::hash<Type>,
     typename = std::equal_to<>,
-    typename = std::allocator<Type>>
+    allocator_like = std::allocator<Type>>
 class dense_set;
 
 template<typename...>

+ 2 - 4
src/entt/container/table.hpp

@@ -170,11 +170,9 @@ public:
 
     /**
      * @brief Constructs the underlying containers using a given allocator.
-     * @tparam Allocator Type of allocator.
      * @param allocator A valid allocator.
      */
-    template<typename Allocator>
-    explicit basic_table(const Allocator &allocator)
+    explicit basic_table(const allocator_like auto &allocator)
         : payload{Container{allocator}...} {}
 
     /**
@@ -424,7 +422,7 @@ private:
 /*! @cond ENTT_INTERNAL */
 namespace std {
 
-template<typename... Container, typename Allocator>
+template<typename... Container, entt::allocator_like Allocator>
 struct uses_allocator<entt::basic_table<Container...>, Allocator>
     : std::bool_constant<(std::uses_allocator_v<Container, Allocator> && ...)> {};
 

+ 22 - 25
src/entt/core/memory.hpp

@@ -7,6 +7,7 @@
 #include <type_traits>
 #include <utility>
 #include "../config/config.h"
+#include "../core/concepts.hpp"
 #include "../stl/memory.hpp"
 
 namespace entt {
@@ -17,7 +18,7 @@ namespace entt {
  * @param lhs A valid allocator.
  * @param rhs Another valid allocator.
  */
-template<typename Allocator>
+template<allocator_like Allocator>
 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;
@@ -30,7 +31,7 @@ constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator
  * @param lhs A valid allocator.
  * @param rhs Another valid allocator.
  */
-template<typename Allocator>
+template<allocator_like Allocator>
 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);
@@ -43,7 +44,7 @@ constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator
  * @param lhs A valid allocator.
  * @param rhs Another valid allocator.
  */
-template<typename Allocator>
+template<allocator_like Allocator>
 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;
@@ -57,7 +58,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma
  * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Allocator>
+template<allocator_like Allocator>
 struct allocation_deleter: private Allocator {
     /*! @brief Allocator type. */
     using allocator_type = Allocator;
@@ -91,7 +92,7 @@ struct allocation_deleter: private Allocator {
  * @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>
+template<typename Type, allocator_like Allocator, typename... Args>
 constexpr auto allocate_unique(Allocator &allocator, Args &&...args) {
     static_assert(!std::is_array_v<Type>, "Array types are not supported");
 
@@ -117,7 +118,7 @@ namespace internal {
 
 template<typename Type>
 struct uses_allocator_construction {
-    template<typename Allocator, typename... Params>
+    template<allocator_like 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)...);
@@ -138,31 +139,30 @@ 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 {
+    template<typename First, typename Second>
+    static constexpr auto args(const allocator_like auto &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 {
+    static constexpr auto args(const allocator_like auto &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 {
+    template<typename First, typename Second>
+    static constexpr auto args(const allocator_like auto &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 {
+    template<typename First, typename Second>
+    static constexpr auto args(const allocator_like auto &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 {
+    template<typename First, typename Second>
+    static constexpr auto args(const allocator_like auto &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)));
     }
 };
@@ -177,14 +177,13 @@ struct uses_allocator_construction<std::pair<Type, Other>> {
  * 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 {
+template<typename Type, typename... Args>
+constexpr auto uses_allocator_construction_args(const allocator_like auto &allocator, Args &&...args) noexcept {
     return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
 }
 
@@ -195,14 +194,13 @@ constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args
  * 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.
  */
-template<typename Type, typename Allocator, typename... Args>
-constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+template<typename Type, typename... Args>
+constexpr Type make_obj_using_allocator(const allocator_like auto &allocator, Args &&...args) {
     return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
 }
 
@@ -213,15 +211,14 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar
  * 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) {
+template<typename Type, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const allocator_like auto &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)...));
 }
 

+ 7 - 7
src/entt/entity/fwd.hpp

@@ -47,10 +47,10 @@ enum class deletion_policy : std::uint8_t {
 template<cvref_unqualified Type, entity_like Entity = entity>
 struct component_traits;
 
-template<entity_like Entity = entity, typename = std::allocator<Entity>>
+template<entity_like Entity = entity, allocator_like = std::allocator<Entity>>
 class basic_sparse_set;
 
-template<typename Type, entity_like = entity, typename = std::allocator<Type>>
+template<typename Type, entity_like = entity, allocator_like = std::allocator<Type>>
 class basic_storage;
 
 template<typename, typename>
@@ -59,13 +59,13 @@ class basic_sigh_mixin;
 template<typename, typename>
 class basic_reactive_mixin;
 
-template<entity_like Entity = entity, typename = std::allocator<Entity>>
+template<entity_like Entity = entity, allocator_like = std::allocator<Entity>>
 class basic_registry;
 
 template<typename, typename>
 class basic_view;
 
-template<typename Type, typename = std::allocator<Type *>>
+template<typename Type, allocator_like = std::allocator<Type *>>
 class basic_runtime_view;
 
 template<typename, typename, typename>
@@ -241,7 +241,7 @@ struct type_list_transform<owned_t<Type...>, Op> {
  * @tparam Entity A valid entity type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Type, entity_like Entity = entity, typename Allocator = std::allocator<Type>>
+template<typename Type, entity_like Entity = entity, allocator_like Allocator = std::allocator<Type>>
 struct storage_type {
     /*! @brief Type-to-storage conversion result. */
     using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>);
@@ -255,7 +255,7 @@ struct reactive final {};
  * @tparam Entity A valid entity type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<entity_like Entity, typename Allocator>
+template<entity_like Entity, allocator_like Allocator>
 struct storage_type<reactive, Entity, Allocator> {
     /*! @brief Type-to-storage conversion result. */
     using type = ENTT_STORAGE(reactive_mixin, basic_storage<reactive, Entity, Allocator>);
@@ -274,7 +274,7 @@ using storage_type_t = storage_type<Args...>::type;
  * @tparam Entity A valid entity type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Type, entity_like Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
+template<typename Type, entity_like Entity = entity, allocator_like 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>;

+ 3 - 2
src/entt/entity/group.hpp

@@ -10,6 +10,7 @@
 #include <utility>
 #include "../config/config.h"
 #include "../core/algorithm.hpp"
+#include "../core/concepts.hpp"
 #include "../core/fwd.hpp"
 #include "../core/iterator.hpp"
 #include "../core/type_info.hpp"
@@ -205,8 +206,8 @@ class group_handler<Type, 0u, Get, Exclude> final: public group_descriptor {
 public:
     using common_type = Type;
 
-    template<typename Allocator, typename... GType, typename... EType>
-    group_handler(const Allocator &allocator, std::tuple<GType &...> gpool, std::tuple<EType &...> epool)
+    template<typename... GType, typename... EType>
+    group_handler(const allocator_like auto &allocator, std::tuple<GType &...> gpool, std::tuple<EType &...> epool)
         : pools{std::apply([](auto &&...cpool) { return std::array<common_type *, Get>{&cpool...}; }, gpool)},
           filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)},
           elem{allocator} {

+ 2 - 2
src/entt/entity/registry.hpp

@@ -131,7 +131,7 @@ private:
     It it;
 };
 
-template<typename Allocator>
+template<allocator_like Allocator>
 class registry_context {
     using alloc_traits = std::allocator_traits<Allocator>;
     using allocator_type = alloc_traits::template rebind_alloc<std::pair<const id_type, basic_any<0u>>>;
@@ -210,7 +210,7 @@ private:
  * @tparam Entity A valid entity type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<entity_like Entity, typename Allocator>
+template<entity_like Entity, allocator_like Allocator>
 class basic_registry {
     using base_type = basic_sparse_set<Entity, Allocator>;
     using alloc_traits = std::allocator_traits<Allocator>;

+ 2 - 1
src/entt/entity/runtime_view.hpp

@@ -6,6 +6,7 @@
 #include <iterator>
 #include <utility>
 #include <vector>
+#include "../core/concepts.hpp"
 #include "entity.hpp"
 #include "fwd.hpp"
 
@@ -115,7 +116,7 @@ private:
  * @tparam Type Common base type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Type, typename Allocator>
+template<typename Type, allocator_like 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");

+ 2 - 1
src/entt/entity/sparse_set.hpp

@@ -13,6 +13,7 @@
 #include "../core/algorithm.hpp"
 #include "../core/any.hpp"
 #include "../core/bit.hpp"
+#include "../core/concepts.hpp"
 #include "../core/type_info.hpp"
 #include "../stl/iterator.hpp"
 #include "entity.hpp"
@@ -136,7 +137,7 @@ private:
  * @tparam Entity A valid entity type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<entity_like Entity, typename Allocator>
+template<entity_like Entity, allocator_like Allocator>
 class basic_sparse_set {
     using alloc_traits = std::allocator_traits<Allocator>;
     static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");

+ 4 - 3
src/entt/entity/storage.hpp

@@ -12,6 +12,7 @@
 #include <vector>
 #include "../config/config.h"
 #include "../core/bit.hpp"
+#include "../core/concepts.hpp"
 #include "../core/iterator.hpp"
 #include "../core/memory.hpp"
 #include "../core/type_info.hpp"
@@ -206,7 +207,7 @@ private:
  * @tparam Entity A valid entity type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Type, entity_like Entity, typename Allocator>
+template<typename Type, entity_like Entity, allocator_like Allocator>
 class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
     using alloc_traits = std::allocator_traits<Allocator>;
     static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
@@ -765,7 +766,7 @@ private:
 };
 
 /*! @copydoc basic_storage */
-template<typename Type, entity_like Entity, typename Allocator>
+template<typename Type, entity_like Entity, allocator_like Allocator>
 requires (component_traits<Type, Entity>::page_size == 0u)
 class basic_storage<Type, Entity, Allocator>
     : public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
@@ -950,7 +951,7 @@ public:
  * @tparam Entity A valid entity type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<entity_like Entity, typename Allocator>
+template<entity_like Entity, allocator_like Allocator>
 class basic_storage<Entity, Entity, Allocator>
     : public basic_sparse_set<Entity, Allocator> {
     using alloc_traits = std::allocator_traits<Allocator>;

+ 2 - 1
src/entt/graph/adjacency_matrix.hpp

@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 #include "../config/config.h"
+#include "../core/concepts.hpp"
 #include "../core/iterator.hpp"
 #include "fwd.hpp"
 
@@ -84,7 +85,7 @@ private:
  * @tparam Category Either a directed or undirected category tag.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<std::derived_from<directed_tag> Category, typename Allocator>
+template<std::derived_from<directed_tag> Category, allocator_like Allocator>
 class adjacency_matrix {
     using alloc_traits = std::allocator_traits<Allocator>;
     static_assert(std::is_same_v<typename alloc_traits::value_type, std::size_t>, "Invalid value type");

+ 2 - 1
src/entt/graph/flow.hpp

@@ -14,6 +14,7 @@
 #include "../container/dense_map.hpp"
 #include "../container/dense_set.hpp"
 #include "../core/compressed_pair.hpp"
+#include "../core/concepts.hpp"
 #include "../core/fwd.hpp"
 #include "../core/iterator.hpp"
 #include "../stl/functional.hpp"
@@ -27,7 +28,7 @@ namespace entt {
  * @brief Utility class for creating task graphs.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Allocator>
+template<allocator_like 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");

+ 3 - 2
src/entt/graph/fwd.hpp

@@ -4,6 +4,7 @@
 #include <concepts>
 #include <cstddef>
 #include <memory>
+#include "../core/concepts.hpp"
 #include "../core/fwd.hpp"
 
 namespace entt {
@@ -14,10 +15,10 @@ struct directed_tag {};
 /*! @brief Directed graph category tag. */
 struct undirected_tag: directed_tag {};
 
-template<std::derived_from<directed_tag>, typename = std::allocator<std::size_t>>
+template<std::derived_from<directed_tag>, allocator_like = std::allocator<std::size_t>>
 class adjacency_matrix;
 
-template<typename = std::allocator<id_type>>
+template<allocator_like = std::allocator<id_type>>
 class basic_flow;
 
 /*! @brief Alias declaration for the most common use case. */

+ 3 - 3
src/entt/locator/locator.hpp

@@ -5,6 +5,7 @@
 #include <memory>
 #include <utility>
 #include "../config/config.h"
+#include "../core/concepts.hpp"
 
 namespace entt {
 
@@ -108,15 +109,14 @@ public:
     /**
      * @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.
      */
-    template<std::derived_from<Service> Type = Service, typename Allocator, typename... Args>
+    template<std::derived_from<Service> Type = Service, typename... Args>
     requires std::constructible_from<Type, Args...>
-    static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) {
+    static Service &emplace(std::allocator_arg_t, allocator_like auto alloc, Args &&...args) {
         service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...);
         return *service;
     }

+ 3 - 2
src/entt/process/fwd.hpp

@@ -3,16 +3,17 @@
 
 #include <cstdint>
 #include <memory>
+#include "../core/concepts.hpp"
 
 namespace entt {
 
-template<typename, typename = std::allocator<void>>
+template<typename, allocator_like = std::allocator<void>>
 class basic_process;
 
 /*! @brief Alias declaration for the most common use case. */
 using process = basic_process<std::uint32_t>;
 
-template<typename, typename = std::allocator<void>>
+template<typename, allocator_like = std::allocator<void>>
 class basic_scheduler;
 
 /*! @brief Alias declaration for the most common use case. */

+ 4 - 3
src/entt/process/process.hpp

@@ -6,6 +6,7 @@
 #include <type_traits>
 #include <utility>
 #include "../core/compressed_pair.hpp"
+#include "../core/concepts.hpp"
 #include "../core/type_traits.hpp"
 #include "fwd.hpp"
 
@@ -14,7 +15,7 @@ namespace entt {
 /*! @cond ENTT_INTERNAL */
 namespace internal {
 
-template<typename, typename, typename>
+template<typename, typename, allocator_like>
 struct process_adaptor;
 
 } // namespace internal
@@ -68,7 +69,7 @@ struct process_adaptor;
  * @tparam Delta Type to use to provide elapsed time.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Delta, typename Allocator>
+template<typename Delta, allocator_like Allocator>
 class basic_process: public std::enable_shared_from_this<basic_process<Delta, Allocator>> {
     enum class state : std::uint8_t {
         idle = 0,
@@ -290,7 +291,7 @@ private:
 /*! @cond ENTT_INTERNAL */
 namespace internal {
 
-template<typename Delta, typename Func, typename Allocator>
+template<typename Delta, typename Func, allocator_like Allocator>
 struct process_adaptor: public basic_process<Delta, Allocator> {
     using allocator_type = Allocator;
     using base_type = basic_process<Delta, Allocator>;

+ 2 - 1
src/entt/process/scheduler.hpp

@@ -8,6 +8,7 @@
 #include <vector>
 #include "../config/config.h"
 #include "../core/compressed_pair.hpp"
+#include "../core/concepts.hpp"
 #include "fwd.hpp"
 #include "process.hpp"
 
@@ -32,7 +33,7 @@ namespace entt {
  * @tparam Delta Type to use to provide elapsed time.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Delta, typename Allocator>
+template<typename Delta, allocator_like Allocator>
 class basic_scheduler {
     using base_type = basic_process<Delta, Allocator>;
     using alloc_traits = std::allocator_traits<Allocator>;

+ 2 - 1
src/entt/resource/cache.hpp

@@ -12,6 +12,7 @@
 #include <utility>
 #include "../container/dense_map.hpp"
 #include "../core/compressed_pair.hpp"
+#include "../core/concepts.hpp"
 #include "../core/fwd.hpp"
 #include "../core/iterator.hpp"
 #include "../stl/functional.hpp"
@@ -123,7 +124,7 @@ private:
  * @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>
+template<typename Type, typename Loader, allocator_like Allocator>
 class resource_cache {
     using alloc_traits = std::allocator_traits<Allocator>;
     static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");

+ 2 - 1
src/entt/resource/fwd.hpp

@@ -2,13 +2,14 @@
 #define ENTT_RESOURCE_FWD_HPP
 
 #include <memory>
+#include "../core/concepts.hpp"
 
 namespace entt {
 
 template<typename>
 struct resource_loader;
 
-template<typename Type, typename = resource_loader<Type>, typename = std::allocator<Type>>
+template<typename Type, typename = resource_loader<Type>, allocator_like = std::allocator<Type>>
 class resource_cache;
 
 template<typename>

+ 2 - 2
src/entt/signal/dispatcher.hpp

@@ -29,7 +29,7 @@ struct basic_dispatcher_handler {
     [[nodiscard]] virtual std::size_t size() const noexcept = 0;
 };
 
-template<cvref_unqualified Type, typename Allocator>
+template<cvref_unqualified Type, allocator_like Allocator>
 class dispatcher_handler final: public basic_dispatcher_handler {
     using alloc_traits = std::allocator_traits<Allocator>;
     using signal_type = sigh<void(Type &), Allocator>;
@@ -102,7 +102,7 @@ private:
  *
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Allocator>
+template<allocator_like Allocator>
 class basic_dispatcher {
     template<typename Type>
     using handler_type = internal::dispatcher_handler<Type, Allocator>;

+ 2 - 1
src/entt/signal/emitter.hpp

@@ -6,6 +6,7 @@
 #include <utility>
 #include "../container/dense_map.hpp"
 #include "../core/compressed_pair.hpp"
+#include "../core/concepts.hpp"
 #include "../core/fwd.hpp"
 #include "../core/type_info.hpp"
 #include "../stl/functional.hpp"
@@ -32,7 +33,7 @@ namespace entt {
  * @tparam Derived Emitter type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Derived, typename Allocator>
+template<typename Derived, allocator_like Allocator>
 class emitter {
     using key_type = id_type;
     using mapped_type = std::function<void(void *)>;

+ 4 - 3
src/entt/signal/fwd.hpp

@@ -2,16 +2,17 @@
 #define ENTT_SIGNAL_FWD_HPP
 
 #include <memory>
+#include "../core/concepts.hpp"
 
 namespace entt {
 
 template<typename>
 class delegate;
 
-template<typename = std::allocator<void>>
+template<allocator_like = std::allocator<void>>
 class basic_dispatcher;
 
-template<typename, typename = std::allocator<void>>
+template<typename, allocator_like = std::allocator<void>>
 class emitter;
 
 class connection;
@@ -21,7 +22,7 @@ struct scoped_connection;
 template<typename>
 class sink;
 
-template<typename Type, typename = std::allocator<void>>
+template<typename Type, allocator_like = std::allocator<void>>
 class sigh;
 
 /*! @brief Alias declaration for the most common use case. */

+ 5 - 4
src/entt/signal/sigh.hpp

@@ -6,6 +6,7 @@
 #include <type_traits>
 #include <utility>
 #include <vector>
+#include "../core/concepts.hpp"
 #include "delegate.hpp"
 #include "fwd.hpp"
 
@@ -31,7 +32,7 @@ class sink;
  * @tparam Type A valid function type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Type, typename Allocator>
+template<typename Type, allocator_like Allocator>
 class sigh;
 
 /**
@@ -50,7 +51,7 @@ class sigh;
  * @tparam Args Types of arguments of a function type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Ret, typename... Args, typename Allocator>
+template<typename Ret, typename... Args, allocator_like Allocator>
 class sigh<Ret(Args...), Allocator> {
     friend class sink<sigh<Ret(Args...), Allocator>>;
 
@@ -354,7 +355,7 @@ private:
  * @tparam Args Types of arguments of a function type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Ret, typename... Args, typename Allocator>
+template<typename Ret, typename... Args, allocator_like Allocator>
 class sink<sigh<Ret(Args...), Allocator>> {
     using signal_type = sigh<Ret(Args...), Allocator>;
     using delegate_type = signal_type::delegate_type;
@@ -565,7 +566,7 @@ private:
  * @tparam Args Types of arguments of a function type.
  * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Ret, typename... Args, typename Allocator>
+template<typename Ret, typename... Args, allocator_like Allocator>
 sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>;
 
 } // namespace entt

+ 6 - 5
src/entt/tools/davey.hpp

@@ -6,6 +6,7 @@
 #include <sstream>
 #include <string>
 #include <imgui.h>
+#include "../core/concepts.hpp"
 #include "../entity/mixin.hpp"
 #include "../entity/registry.hpp"
 #include "../entity/sparse_set.hpp"
@@ -133,7 +134,7 @@ static void present_element(const meta_any &obj, OnEntity on_entity) {
     }
 }
 
-template<entity_like Entity, typename Allocator>
+template<entity_like Entity, allocator_like Allocator>
 static void present_storage(const meta_ctx &ctx, const basic_sparse_set<Entity, Allocator> &storage) {
     if(auto type = resolve(ctx, storage.info()); type) {
         for(auto entt: storage) {
@@ -234,7 +235,7 @@ static void present_view(const meta_ctx &ctx, const basic_view<get_t<Get...>, ex
  * @param ctx The context from which to search for meta types.
  * @param storage An instance of the storage type.
  */
-template<typename Type, entity_like Entity, typename Allocator>
+template<typename Type, entity_like Entity, allocator_like Allocator>
 void davey(const meta_ctx &ctx, const basic_storage<Type, Entity, Allocator> &storage) {
     internal::present_storage(ctx, storage);
 }
@@ -246,7 +247,7 @@ void davey(const meta_ctx &ctx, const basic_storage<Type, Entity, Allocator> &st
  * @tparam Allocator Storage allocator type.
  * @param storage An instance of the storage type.
  */
-template<typename Type, entity_like Entity, typename Allocator>
+template<typename Type, entity_like Entity, allocator_like Allocator>
 void davey(const basic_storage<Type, Entity, Allocator> &storage) {
     davey(locator<meta_ctx>::value_or(), storage);
 }
@@ -281,7 +282,7 @@ void davey(const basic_view<get_t<Get...>, exclude_t<Exclude...>> &view) {
  * @param ctx The context from which to search for meta types.
  * @param registry An instance of the registry type.
  */
-template<entity_like Entity, typename Allocator>
+template<entity_like Entity, allocator_like Allocator>
 void davey(const meta_ctx &ctx, const basic_registry<Entity, Allocator> &registry) {
     ImGui::BeginTabBar("#tabs");
 
@@ -325,7 +326,7 @@ void davey(const meta_ctx &ctx, const basic_registry<Entity, Allocator> &registr
  * @tparam Allocator Registry allocator type.
  * @param registry An instance of the registry type.
  */
-template<entity_like Entity, typename Allocator>
+template<entity_like Entity, allocator_like Allocator>
 void davey(const basic_registry<Entity, Allocator> &registry) {
     davey(locator<meta_ctx>::value_or(), registry);
 }