memory.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. #ifndef ENTT_CORE_MEMORY_HPP
  2. #define ENTT_CORE_MEMORY_HPP
  3. #include <cstddef>
  4. #include <limits>
  5. #include <memory>
  6. #include <tuple>
  7. #include <type_traits>
  8. #include <utility>
  9. #include "../config/config.h"
  10. namespace entt {
  11. /**
  12. * @brief Checks whether a value is a power of two or not (waiting for C++20 and
  13. * `std::has_single_bit`).
  14. * @param value A value that may or may not be a power of two.
  15. * @return True if the value is a power of two, false otherwise.
  16. */
  17. [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept {
  18. return value && ((value & (value - 1)) == 0);
  19. }
  20. /**
  21. * @brief Computes the smallest power of two greater than or equal to a value
  22. * (waiting for C++20 and `std::bit_ceil`).
  23. * @param value The value to use.
  24. * @return The smallest power of two greater than or equal to the given value.
  25. */
  26. [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
  27. ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
  28. std::size_t curr = value - (value != 0u);
  29. for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
  30. curr |= curr >> next;
  31. }
  32. return ++curr;
  33. }
  34. /**
  35. * @brief Fast module utility function (powers of two only).
  36. * @param value A value for which to calculate the modulus.
  37. * @param mod _Modulus_, it must be a power of two.
  38. * @return The common remainder.
  39. */
  40. [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
  41. ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two");
  42. return value & (mod - 1u);
  43. }
  44. /**
  45. * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
  46. * @tparam Type Pointer type.
  47. * @param ptr Fancy or raw pointer.
  48. * @return A raw pointer that represents the address of the original pointer.
  49. */
  50. template<typename Type>
  51. [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept {
  52. if constexpr(std::is_pointer_v<std::decay_t<Type>>) {
  53. return ptr;
  54. } else {
  55. return to_address(std::forward<Type>(ptr).operator->());
  56. }
  57. }
  58. /**
  59. * @brief Utility function to design allocation-aware containers.
  60. * @tparam Allocator Type of allocator.
  61. * @param lhs A valid allocator.
  62. * @param rhs Another valid allocator.
  63. */
  64. template<typename Allocator>
  65. constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
  66. if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
  67. lhs = rhs;
  68. }
  69. }
  70. /**
  71. * @brief Utility function to design allocation-aware containers.
  72. * @tparam Allocator Type of allocator.
  73. * @param lhs A valid allocator.
  74. * @param rhs Another valid allocator.
  75. */
  76. template<typename Allocator>
  77. constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
  78. if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
  79. lhs = std::move(rhs);
  80. }
  81. }
  82. /**
  83. * @brief Utility function to design allocation-aware containers.
  84. * @tparam Allocator Type of allocator.
  85. * @param lhs A valid allocator.
  86. * @param rhs Another valid allocator.
  87. */
  88. template<typename Allocator>
  89. constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
  90. if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
  91. using std::swap;
  92. swap(lhs, rhs);
  93. } else {
  94. ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers");
  95. }
  96. }
  97. /**
  98. * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
  99. * @tparam Allocator Type of allocator used to manage memory and elements.
  100. */
  101. template<typename Allocator>
  102. struct allocation_deleter: private Allocator {
  103. /*! @brief Allocator type. */
  104. using allocator_type = Allocator;
  105. /*! @brief Pointer type. */
  106. using pointer = typename std::allocator_traits<Allocator>::pointer;
  107. /**
  108. * @brief Inherited constructors.
  109. * @param alloc The allocator to use.
  110. */
  111. constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v<allocator_type>)
  112. : Allocator{alloc} {}
  113. /**
  114. * @brief Destroys the pointed object and deallocates its memory.
  115. * @param ptr A valid pointer to an object of the given type.
  116. */
  117. constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
  118. using alloc_traits = std::allocator_traits<Allocator>;
  119. alloc_traits::destroy(*this, to_address(ptr));
  120. alloc_traits::deallocate(*this, ptr, 1u);
  121. }
  122. };
  123. /**
  124. * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
  125. * @tparam Type Type of object to allocate for and to construct.
  126. * @tparam Allocator Type of allocator used to manage memory and elements.
  127. * @tparam Args Types of arguments to use to construct the object.
  128. * @param allocator The allocator to use.
  129. * @param args Parameters to use to construct the object.
  130. * @return A properly initialized unique pointer with a custom deleter.
  131. */
  132. template<typename Type, typename Allocator, typename... Args>
  133. ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
  134. static_assert(!std::is_array_v<Type>, "Array types are not supported");
  135. using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
  136. using allocator_type = typename alloc_traits::allocator_type;
  137. allocator_type alloc{allocator};
  138. auto ptr = alloc_traits::allocate(alloc, 1u);
  139. ENTT_TRY {
  140. alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
  141. }
  142. ENTT_CATCH {
  143. alloc_traits::deallocate(alloc, ptr, 1u);
  144. ENTT_THROW;
  145. }
  146. return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
  147. }
  148. /**
  149. * @cond TURN_OFF_DOXYGEN
  150. * Internal details not to be documented.
  151. */
  152. namespace internal {
  153. template<typename Type>
  154. struct uses_allocator_construction {
  155. template<typename Allocator, typename... Params>
  156. static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept {
  157. if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
  158. return std::forward_as_tuple(std::forward<Params>(params)...);
  159. } else {
  160. static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
  161. if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
  162. return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>{std::allocator_arg, allocator, std::forward<Params>(params)...};
  163. } else {
  164. static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
  165. return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
  166. }
  167. }
  168. }
  169. };
  170. template<typename Type, typename Other>
  171. struct uses_allocator_construction<std::pair<Type, Other>> {
  172. using type = std::pair<Type, Other>;
  173. template<typename Allocator, typename First, typename Second>
  174. static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept {
  175. return std::make_tuple(
  176. std::piecewise_construct,
  177. std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
  178. std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
  179. }
  180. template<typename Allocator>
  181. static constexpr auto args(const Allocator &allocator) noexcept {
  182. return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
  183. }
  184. template<typename Allocator, typename First, typename Second>
  185. static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept {
  186. 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)));
  187. }
  188. template<typename Allocator, typename First, typename Second>
  189. static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) noexcept {
  190. return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
  191. }
  192. template<typename Allocator, typename First, typename Second>
  193. static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) noexcept {
  194. 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)));
  195. }
  196. };
  197. } // namespace internal
  198. /**
  199. * Internal details not to be documented.
  200. * @endcond
  201. */
  202. /**
  203. * @brief Uses-allocator construction utility (waiting for C++20).
  204. *
  205. * Primarily intended for internal use. Prepares the argument list needed to
  206. * create an object of a given type by means of uses-allocator construction.
  207. *
  208. * @tparam Type Type to return arguments for.
  209. * @tparam Allocator Type of allocator used to manage memory and elements.
  210. * @tparam Args Types of arguments to use to construct the object.
  211. * @param allocator The allocator to use.
  212. * @param args Parameters to use to construct the object.
  213. * @return The arguments needed to create an object of the given type.
  214. */
  215. template<typename Type, typename Allocator, typename... Args>
  216. constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept {
  217. return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
  218. }
  219. /**
  220. * @brief Uses-allocator construction utility (waiting for C++20).
  221. *
  222. * Primarily intended for internal use. Creates an object of a given type by
  223. * means of uses-allocator construction.
  224. *
  225. * @tparam Type Type of object to create.
  226. * @tparam Allocator Type of allocator used to manage memory and elements.
  227. * @tparam Args Types of arguments to use to construct the object.
  228. * @param allocator The allocator to use.
  229. * @param args Parameters to use to construct the object.
  230. * @return A newly created object of the given type.
  231. */
  232. template<typename Type, typename Allocator, typename... Args>
  233. constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
  234. return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
  235. }
  236. /**
  237. * @brief Uses-allocator construction utility (waiting for C++20).
  238. *
  239. * Primarily intended for internal use. Creates an object of a given type by
  240. * means of uses-allocator construction at an uninitialized memory location.
  241. *
  242. * @tparam Type Type of object to create.
  243. * @tparam Allocator Type of allocator used to manage memory and elements.
  244. * @tparam Args Types of arguments to use to construct the object.
  245. * @param value Memory location in which to place the object.
  246. * @param allocator The allocator to use.
  247. * @param args Parameters to use to construct the object.
  248. * @return A pointer to the newly created object of the given type.
  249. */
  250. template<typename Type, typename Allocator, typename... Args>
  251. constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
  252. 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)...));
  253. }
  254. } // namespace entt
  255. #endif