EnTT 3.13.0
Loading...
Searching...
No Matches
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
12namespace entt {
13
20[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept {
21 return value && ((value & (value - 1)) == 0);
22}
23
30[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
31 ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
32 std::size_t curr = value - (value != 0u);
33
34 for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
35 curr |= curr >> next;
36 }
37
38 return ++curr;
39}
40
47[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
48 ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two");
49 return value & (mod - 1u);
50}
51
58template<typename Type>
59[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept {
60 if constexpr(std::is_pointer_v<std::decay_t<Type>>) {
61 return ptr;
62 } else {
63 return to_address(std::forward<Type>(ptr).operator->());
64 }
65}
66
73template<typename Allocator>
74constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
75 if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
76 lhs = rhs;
77 }
78}
79
86template<typename Allocator>
87constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
88 if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
89 lhs = std::move(rhs);
90 }
91}
92
99template<typename Allocator>
100constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
101 if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
102 using std::swap;
103 swap(lhs, rhs);
104 } else {
105 ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers");
106 }
107}
108
113template<typename Allocator>
114struct allocation_deleter: private Allocator {
116 using allocator_type = Allocator;
118 using pointer = typename std::allocator_traits<Allocator>::pointer;
119
124 constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v<allocator_type>)
125 : Allocator{alloc} {}
126
131 constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
132 using alloc_traits = std::allocator_traits<Allocator>;
133 alloc_traits::destroy(*this, to_address(ptr));
134 alloc_traits::deallocate(*this, ptr, 1u);
135 }
136};
137
147template<typename Type, typename Allocator, typename... Args>
148ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
149 static_assert(!std::is_array_v<Type>, "Array types are not supported");
150
151 using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
152 using allocator_type = typename alloc_traits::allocator_type;
153
154 allocator_type alloc{allocator};
155 auto ptr = alloc_traits::allocate(alloc, 1u);
156
157 ENTT_TRY {
158 alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
159 }
160 ENTT_CATCH {
161 alloc_traits::deallocate(alloc, ptr, 1u);
162 ENTT_THROW;
163 }
164
165 return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
166}
167
169namespace internal {
170
171template<typename Type>
172struct uses_allocator_construction {
173 template<typename Allocator, typename... Params>
174 static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept {
175 if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
176 return std::forward_as_tuple(std::forward<Params>(params)...);
177 } else {
178 static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
179
180 if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
181 return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>{std::allocator_arg, allocator, std::forward<Params>(params)...};
182 } else {
183 static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
184 return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
185 }
186 }
187 }
188};
189
190template<typename Type, typename Other>
191struct uses_allocator_construction<std::pair<Type, Other>> {
192 using type = std::pair<Type, Other>;
193
194 template<typename Allocator, typename First, typename Second>
195 static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept {
196 return std::make_tuple(
197 std::piecewise_construct,
198 std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
199 std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
200 }
201
202 template<typename Allocator>
203 static constexpr auto args(const Allocator &allocator) noexcept {
204 return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
205 }
206
207 template<typename Allocator, typename First, typename Second>
208 static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept {
209 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)));
210 }
211
212 template<typename Allocator, typename First, typename Second>
213 static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) noexcept {
214 return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
215 }
216
217 template<typename Allocator, typename First, typename Second>
218 static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) noexcept {
219 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)));
220 }
221};
222
223} // namespace internal
239template<typename Type, typename Allocator, typename... Args>
240constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept {
241 return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
242}
243
257template<typename Type, typename Allocator, typename... Args>
258constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
259 return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
260}
261
276template<typename Type, typename Allocator, typename... Args>
277constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
278 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)...));
279}
280
281} // namespace entt
282
283#endif
EnTT default namespace.
Definition dense_map.hpp:21
constexpr void propagate_on_container_swap(Allocator &lhs, Allocator &rhs) noexcept
Utility function to design allocation-aware containers.
Definition memory.hpp:100
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
Definition memory.hpp:258
constexpr void propagate_on_container_move_assignment(Allocator &lhs, Allocator &rhs) noexcept
Utility function to design allocation-aware containers.
Definition memory.hpp:87
constexpr bool is_power_of_two(const std::size_t value) noexcept
Checks whether a value is a power of two or not (waiting for C++20 and std::has_single_bit).
Definition memory.hpp:20
constexpr Type * uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
Definition memory.hpp:277
constexpr void swap(compressed_pair< First, Second > &lhs, compressed_pair< First, Second > &rhs)
Swaps two compressed pair objects.
constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept
Fast module utility function (powers of two only).
Definition memory.hpp:47
constexpr std::size_t next_power_of_two(const std::size_t value) noexcept
Computes the smallest power of two greater than or equal to a value (waiting for C++20 and std::bit_c...
Definition memory.hpp:30
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept
Uses-allocator construction utility (waiting for C++20).
Definition memory.hpp:240
constexpr auto allocate_unique(Allocator &allocator, Args &&...args)
Allows std::unique_ptr to use allocators (waiting for C++20).
Definition memory.hpp:148
constexpr void propagate_on_container_copy_assignment(Allocator &lhs, Allocator &rhs) noexcept
Utility function to design allocation-aware containers.
Definition memory.hpp:74
constexpr auto to_address(Type &&ptr) noexcept
Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
Definition memory.hpp:59
Deleter for allocator-aware unique pointers (waiting for C++20).
Definition memory.hpp:114
typename std::allocator_traits< Allocator >::pointer pointer
Pointer type.
Definition memory.hpp:118
constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v< allocator_type >)
Inherited constructors.
Definition memory.hpp:124
constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v< typename allocator_type::value_type >)
Destroys the pointed object and deallocates its memory.
Definition memory.hpp:131
Allocator allocator_type
Allocator type.
Definition memory.hpp:116