#ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include "../config/config.h" #include "../stl/cstddef.hpp" #include "../stl/memory.hpp" #include "../stl/tuple.hpp" #include "../stl/type_traits.hpp" #include "../stl/utility.hpp" namespace entt { /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(stl::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(stl::allocator_traits::propagate_on_container_move_assignment::value) { lhs = stl::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(stl::allocator_traits::propagate_on_container_swap::value) { using stl::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = stl::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(stl::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(stl::is_nothrow_destructible_v) { using alloc_traits = stl::allocator_traits; alloc_traits::destroy(*this, stl::to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `stl::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @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 properly initialized unique pointer with a custom deleter. */ template constexpr auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!stl::is_array_v, "Array types are not supported"); using alloc_traits = stl::allocator_traits::template rebind_traits; using allocator_type = alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, stl::to_address(ptr), stl::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return stl::unique_ptr>{ptr, alloc}; } /*! @cond ENTT_INTERNAL */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!stl::uses_allocator_v && stl::is_constructible_v) { return stl::forward_as_tuple(stl::forward(params)...); } else { static_assert(stl::uses_allocator_v, "Ill-formed request"); if constexpr(stl::is_constructible_v) { return stl::tuple{stl::allocator_arg, allocator, stl::forward(params)...}; } else { static_assert(stl::is_constructible_v, "Ill-formed request"); return stl::forward_as_tuple(stl::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = stl::pair; template static constexpr auto args(const auto &allocator, stl::piecewise_construct_t, First &&first, Second &&second) noexcept { return stl::make_tuple( stl::piecewise_construct, stl::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, stl::forward(curr)...); }, stl::forward(first)), stl::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, stl::forward(curr)...); }, stl::forward(second))); } static constexpr auto args(const auto &allocator) noexcept { return uses_allocator_construction::args(allocator, stl::piecewise_construct, stl::tuple<>{}, stl::tuple<>{}); } template static constexpr auto args(const auto &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, stl::piecewise_construct, stl::forward_as_tuple(stl::forward(first)), stl::forward_as_tuple(stl::forward(second))); } template static constexpr auto args(const auto &allocator, const stl::pair &value) noexcept { return uses_allocator_construction::args(allocator, stl::piecewise_construct, stl::forward_as_tuple(value.first), stl::forward_as_tuple(value.second)); } template static constexpr auto args(const auto &allocator, stl::pair &&value) noexcept { return uses_allocator_construction::args(allocator, stl::piecewise_construct, stl::forward_as_tuple(stl::move(value.first)), stl::forward_as_tuple(stl::move(value.second))); } }; } // namespace internal /*! @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @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 constexpr auto uses_allocator_construction_args(const auto &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, stl::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @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 constexpr Type make_obj_using_allocator(const auto &allocator, Args &&...args) { return stl::make_from_tuple(internal::uses_allocator_construction::args(allocator, stl::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @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 constexpr Type *uninitialized_construct_using_allocator(Type *value, const auto &allocator, Args &&...args) { return stl::apply([value](auto &&...curr) { return ::new(value) Type(stl::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, stl::forward(args)...)); } } // namespace entt #endif