1 #ifndef ENTT_CORE_MEMORY_HPP
2 #define ENTT_CORE_MEMORY_HPP
10 #include "../config/config.h"
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>>>) {
25 return to_address(std::forward<Type>(ptr).operator->());
35 template<
typename Allocator>
37 if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
48 template<
typename Allocator>
50 if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
61 template<
typename Allocator>
63 ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs,
"Cannot swap the containers");
65 if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
76 [[nodiscard]]
inline constexpr
bool is_power_of_two(
const std::size_t value) ENTT_NOEXCEPT {
77 return value && ((value & (value - 1)) == 0);
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);
89 for(
int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
102 [[nodiscard]]
inline constexpr std::size_t
fast_mod(
const std::size_t value,
const std::size_t mod) ENTT_NOEXCEPT {
104 return value & (mod - 1u);
111 template<
typename Allocator>
116 using pointer =
typename std::allocator_traits<Allocator>::pointer;
123 : Allocator{alloc} {}
130 using alloc_traits =
typename std::allocator_traits<Allocator>;
131 alloc_traits::destroy(*
this,
to_address(ptr));
132 alloc_traits::deallocate(*
this, ptr, 1u);
145 template<
typename Type,
typename Allocator,
typename... Args>
147 static_assert(!std::is_array_v<Type>,
"Array types are not supported");
149 using alloc_traits =
typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
150 using allocator_type =
typename alloc_traits::allocator_type;
152 allocator_type alloc{allocator};
153 auto ptr = alloc_traits::allocate(alloc, 1u);
156 alloc_traits::construct(alloc,
to_address(ptr), std::forward<Args>(args)...);
159 alloc_traits::deallocate(alloc, ptr, 1u);
163 return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
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)...);
180 static_assert(std::uses_allocator_v<Type, Allocator>,
"Ill-formed request");
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)...);
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);
192 template<
typename Type,
typename Other>
193 struct uses_allocator_construction<std::pair<Type, Other>> {
194 using type = std::pair<Type, Other>;
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)));
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<>{});
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)));
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));
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)));
245 template<
typename Type,
typename Allocator,
typename... Args>
247 return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
263 template<
typename Type,
typename Allocator,
typename... Args>
265 return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
282 template<
typename Type,
typename Allocator,
typename... 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)...));
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs)
Utility function to design allocation-aware containers.
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs)
Utility function to design allocation-aware containers.
constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod)
Fast module utility function (powers of two only).
auto allocate_unique(Allocator &allocator, Args &&...args)
Allows std::unique_ptr to use allocators (waiting for C++20).
constexpr bool is_power_of_two(const std::size_t value)
Checks whether a value is a power of two or not.
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.
constexpr Type * uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
constexpr auto to_address(Type &&ptr)
Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs)
Utility function to design allocation-aware containers.
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
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).
allocation_deleter(const allocator_type &alloc)
Inherited constructors.
typename std::allocator_traits< Allocator >::pointer pointer
Pointer type.
Allocator allocator_type
Allocator type.
void operator()(pointer ptr)
Destroys the pointed object and deallocates its memory.