EnTT  3.10.0
memory.hpp
1 #ifndef ENTT_CORE_MEMORY_HPP
2 #define ENTT_CORE_MEMORY_HPP
3 
4 #include <cstddef>
5 #include <limits>
6 #include <memory>
7 #include <tuple>
8 #include <type_traits>
9 #include <utility>
10 #include "../config/config.h"
11 
12 namespace entt {
13 
20 template<typename Type>
21 [[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
22  if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
23  return ptr;
24  } else {
25  return to_address(std::forward<Type>(ptr).operator->());
26  }
27 }
28 
35 template<typename Allocator>
36 constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
37  if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
38  lhs = rhs;
39  }
40 }
41 
48 template<typename Allocator>
49 constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
50  if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
51  lhs = std::move(rhs);
52  }
53 }
54 
61 template<typename Allocator>
62 constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
63  ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
64 
65  if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
66  using std::swap;
67  swap(lhs, rhs);
68  }
69 }
70 
76 [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
77  return value && ((value & (value - 1)) == 0);
78 }
79 
85 [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
86  ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
87  std::size_t curr = value - (value != 0u);
88 
89  for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
90  curr |= curr >> next;
91  }
92 
93  return ++curr;
94 }
95 
102 [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
103  ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
104  return value & (mod - 1u);
105 }
106 
111 template<typename Allocator>
112 struct allocation_deleter: private Allocator {
114  using allocator_type = Allocator;
116  using pointer = typename std::allocator_traits<Allocator>::pointer;
117 
123  : Allocator{alloc} {}
124 
129  void operator()(pointer ptr) {
130  using alloc_traits = typename std::allocator_traits<Allocator>;
131  alloc_traits::destroy(*this, to_address(ptr));
132  alloc_traits::deallocate(*this, ptr, 1u);
133  }
134 };
135 
145 template<typename Type, typename Allocator, typename... Args>
146 auto allocate_unique(Allocator &allocator, Args &&...args) {
147  static_assert(!std::is_array_v<Type>, "Array types are not supported");
148 
149  using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
150  using allocator_type = typename alloc_traits::allocator_type;
151 
152  allocator_type alloc{allocator};
153  auto ptr = alloc_traits::allocate(alloc, 1u);
154 
155  ENTT_TRY {
156  alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
157  }
158  ENTT_CATCH {
159  alloc_traits::deallocate(alloc, ptr, 1u);
160  ENTT_THROW;
161  }
162 
163  return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
164 }
165 
171 namespace internal {
172 
173 template<typename Type>
174 struct uses_allocator_construction {
175  template<typename Allocator, typename... Params>
176  static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
177  if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
178  return std::forward_as_tuple(std::forward<Params>(params)...);
179  } else {
180  static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
181 
182  if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
183  return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
184  } else {
185  static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
186  return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
187  }
188  }
189  }
190 };
191 
192 template<typename Type, typename Other>
193 struct uses_allocator_construction<std::pair<Type, Other>> {
194  using type = std::pair<Type, Other>;
195 
196  template<typename Allocator, typename First, typename Second>
197  static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
198  return std::make_tuple(
199  std::piecewise_construct,
200  std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
201  std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
202  }
203 
204  template<typename Allocator>
205  static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
206  return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
207  }
208 
209  template<typename Allocator, typename First, typename Second>
210  static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
211  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)));
212  }
213 
214  template<typename Allocator, typename First, typename Second>
215  static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
216  return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
217  }
218 
219  template<typename Allocator, typename First, typename Second>
220  static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
221  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)));
222  }
223 };
224 
225 } // namespace internal
226 
245 template<typename Type, typename Allocator, typename... Args>
246 constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
247  return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
248 }
249 
263 template<typename Type, typename Allocator, typename... Args>
264 constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
265  return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
266 }
267 
282 template<typename Type, typename Allocator, typename... Args>
283 constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
284  return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
285 }
286 
287 } // namespace entt
288 
289 #endif
EnTT default namespace.
Definition: dense_map.hpp:22
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs)
Utility function to design allocation-aware containers.
Definition: memory.hpp:49
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
Definition: memory.hpp:264
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs)
Utility function to design allocation-aware containers.
Definition: memory.hpp:62
constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod)
Fast module utility function (powers of two only).
Definition: memory.hpp:102
auto allocate_unique(Allocator &allocator, Args &&...args)
Allows std::unique_ptr to use allocators (waiting for C++20).
Definition: memory.hpp:146
constexpr bool is_power_of_two(const std::size_t value)
Checks whether a value is a power of two or not.
Definition: memory.hpp:76
constexpr std::size_t next_power_of_two(const std::size_t value)
Computes the smallest power of two greater than or equal to a value.
Definition: memory.hpp:85
constexpr Type * uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
Definition: memory.hpp:283
constexpr auto to_address(Type &&ptr)
Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
Definition: memory.hpp:21
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs)
Utility function to design allocation-aware containers.
Definition: memory.hpp:36
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
Definition: memory.hpp:246
void swap(compressed_pair< First, Second > &lhs, compressed_pair< First, Second > &rhs)
Swaps two compressed pair objects.
Deleter for allocator-aware unique pointers (waiting for C++20).
Definition: memory.hpp:112
allocation_deleter(const allocator_type &alloc)
Inherited constructors.
Definition: memory.hpp:122
typename std::allocator_traits< Allocator >::pointer pointer
Pointer type.
Definition: memory.hpp:116
Allocator allocator_type
Allocator type.
Definition: memory.hpp:114
void operator()(pointer ptr)
Destroys the pointed object and deallocates its memory.
Definition: memory.hpp:129