1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
// Copyright (C) 2015-2025 Jonathan Müller and foonathan/memory contributors
// SPDX-License-Identifier: Zlib
#ifndef FOONATHAN_MEMORY_SMART_PTR_HPP_INCLUDED
#define FOONATHAN_MEMORY_SMART_PTR_HPP_INCLUDED
/// \file
/// \c std::make_unique() / \c std::make_shared() replacement allocating memory through a \concept{concept_rawallocator,RawAllocator}.
/// \note Only available on a hosted implementation.
#include "config.hpp"
#if !FOONATHAN_HOSTED_IMPLEMENTATION
#error "This header is only available for a hosted implementation."
#endif
#include <memory>
#include <type_traits>
#include "detail/utility.hpp"
#include "deleter.hpp"
#include "std_allocator.hpp"
namespace foonathan
{
namespace memory
{
namespace detail
{
template <typename T, class RawAllocator, typename... Args>
auto allocate_unique(allocator_reference<RawAllocator> alloc, Args&&... args)
-> std::unique_ptr<T, allocator_deleter<T, RawAllocator>>
{
using raw_ptr = std::unique_ptr<T, allocator_deallocator<T, RawAllocator>>;
auto memory = alloc.allocate_node(sizeof(T), alignof(T));
// raw_ptr deallocates memory in case of constructor exception
raw_ptr result(static_cast<T*>(memory), {alloc});
// call constructor
::new (memory) T(detail::forward<Args>(args)...);
// pass ownership to return value using a deleter that calls destructor
return {result.release(), {alloc}};
}
template <typename T, typename... Args>
void construct(std::true_type, T* cur, T* end, Args&&... args)
{
for (; cur != end; ++cur)
::new (static_cast<void*>(cur)) T(detail::forward<Args>(args)...);
}
template <typename T, typename... Args>
void construct(std::false_type, T* begin, T* end, Args&&... args)
{
#if FOONATHAN_HAS_EXCEPTION_SUPPORT
auto cur = begin;
try
{
for (; cur != end; ++cur)
::new (static_cast<void*>(cur)) T(detail::forward<Args>(args)...);
}
catch (...)
{
for (auto el = begin; el != cur; ++el)
el->~T();
throw;
}
#else
construct(std::true_type{}, begin, end, detail::forward<Args>(args)...);
#endif
}
template <typename T, class RawAllocator>
auto allocate_array_unique(std::size_t size, allocator_reference<RawAllocator> alloc)
-> std::unique_ptr<T[], allocator_deleter<T[], RawAllocator>>
{
using raw_ptr = std::unique_ptr<T[], allocator_deallocator<T[], RawAllocator>>;
auto memory = alloc.allocate_array(size, sizeof(T), alignof(T));
// raw_ptr deallocates memory in case of constructor exception
raw_ptr result(static_cast<T*>(memory), {alloc, size});
construct(std::integral_constant<bool, noexcept(T())>{}, result.get(),
result.get() + size);
// pass ownership to return value using a deleter that calls destructor
return {result.release(), {alloc, size}};
}
} // namespace detail
/// A \c std::unique_ptr that deletes using a \concept{concept_rawallocator,RawAllocator}.
///
/// It is an alias template using \ref allocator_deleter as \c Deleter class.
/// \ingroup adapter
template <typename T, class RawAllocator>
FOONATHAN_ALIAS_TEMPLATE(unique_ptr,
std::unique_ptr<T, allocator_deleter<T, RawAllocator>>);
/// A \c std::unique_ptr that deletes using a \concept{concept_rawallocator,RawAllocator} and allows polymorphic types.
///
/// It can only be created by converting a regular unique pointer to a pointer to a derived class,
/// and is meant to be used inside containers.
/// It is an alias template using \ref allocator_polymorphic_deleter as \c Deleter class.
/// \note It has a relatively high overhead, so only use it if you have to.
/// \ingroup adapter
template <class BaseType, class RawAllocator>
FOONATHAN_ALIAS_TEMPLATE(
unique_base_ptr,
std::unique_ptr<BaseType, allocator_polymorphic_deleter<BaseType, RawAllocator>>);
/// Creates a \c std::unique_ptr using a \concept{concept_rawallocator,RawAllocator} for the allocation.
/// \effects Allocates memory for the given type using the allocator
/// and creates a new object inside it passing the given arguments to its constructor.
/// \returns A \c std::unique_ptr owning that memory.
/// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the deleter,
/// the caller has to ensure that the object lives as long as the smart pointer.
/// \ingroup adapter
template <typename T, class RawAllocator, typename... Args>
auto allocate_unique(RawAllocator&& alloc, Args&&... args) -> FOONATHAN_REQUIRES_RET(
!std::is_array<T>::value,
std::unique_ptr<T, allocator_deleter<T, typename std::decay<RawAllocator>::type>>)
{
return detail::allocate_unique<T>(make_allocator_reference(
detail::forward<RawAllocator>(alloc)),
detail::forward<Args>(args)...);
}
/// Creates a \c std::unique_ptr using a type-erased \concept{concept_rawallocator,RawAllocator} for the allocation.
/// It is the same as the other overload but stores the reference to the allocator type-erased inside the \c std::unique_ptr.
/// \effects Allocates memory for the given type using the allocator
/// and creates a new object inside it passing the given arguments to its constructor.
/// \returns A \c std::unique_ptr with a type-erased allocator reference owning that memory.
/// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the deleter,
/// the caller has to ensure that the object lives as long as the smart pointer.
/// \ingroup adapter
template <typename T, class RawAllocator, typename... Args>
auto allocate_unique(any_allocator, RawAllocator&& alloc, Args&&... args)
-> FOONATHAN_REQUIRES_RET(!std::is_array<T>::value,
std::unique_ptr<T, allocator_deleter<T, any_allocator>>)
{
return detail::allocate_unique<T, any_allocator>(make_allocator_reference(
detail::forward<RawAllocator>(
alloc)),
detail::forward<Args>(args)...);
}
/// Creates a \c std::unique_ptr owning an array using a \concept{concept_rawallocator,RawAllocator} for the allocation.
/// \effects Allocates memory for an array of given size and value initializes each element inside of it.
/// \returns A \c std::unique_ptr owning that array.
/// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the deleter,
/// the caller has to ensure that the object lives as long as the smart pointer.
/// \ingroup adapter
template <typename T, class RawAllocator>
auto allocate_unique(RawAllocator&& alloc, std::size_t size) -> FOONATHAN_REQUIRES_RET(
std::is_array<T>::value,
std::unique_ptr<T, allocator_deleter<T, typename std::decay<RawAllocator>::type>>)
{
return detail::allocate_array_unique<
typename std::remove_extent<T>::type>(size,
make_allocator_reference(
detail::forward<RawAllocator>(alloc)));
}
/// Creates a \c std::unique_ptr owning an array using a type-erased \concept{concept_rawallocator,RawAllocator} for the allocation.
/// It is the same as the other overload but stores the reference to the allocator type-erased inside the \c std::unique_ptr.
/// \effects Allocates memory for an array of given size and value initializes each element inside of it.
/// \returns A \c std::unique_ptr with a type-erased allocator reference owning that array.
/// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the deleter,
/// the caller has to ensure that the object lives as long as the smart pointer.
/// \ingroup adapter
template <typename T, class RawAllocator>
auto allocate_unique(any_allocator, RawAllocator&& alloc, std::size_t size)
-> FOONATHAN_REQUIRES_RET(std::is_array<T>::value,
std::unique_ptr<T, allocator_deleter<T, any_allocator>>)
{
return detail::allocate_array_unique<typename std::remove_extent<T>::type,
any_allocator>(size,
make_allocator_reference(
detail::forward<RawAllocator>(
alloc)));
}
/// Creates a \c std::shared_ptr using a \concept{concept_rawallocator,RawAllocator} for the allocation.
/// It is similar to \c std::allocate_shared but uses a \c RawAllocator (and thus also supports any \c Allocator).
/// \effects Calls \ref std_allocator::make_std_allocator to wrap the allocator and forwards to \c std::allocate_shared.
/// \returns A \c std::shared_ptr created using \c std::allocate_shared.
/// \note If the allocator is stateful a reference to the \c RawAllocator will be stored inside the shared pointer,
/// the caller has to ensure that the object lives as long as the smart pointer.
/// \ingroup adapter
template <typename T, class RawAllocator, typename... Args>
std::shared_ptr<T> allocate_shared(RawAllocator&& alloc, Args&&... args)
{
return std::allocate_shared<T>(make_std_allocator<T>(
detail::forward<RawAllocator>(alloc)),
detail::forward<Args>(args)...);
}
} // namespace memory
} // namespace foonathan
#endif // FOONATHAN_MEMORY_SMART_PTR_HPP_INCLUDED
|