Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
stl_allocator_adapter.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/core_pch.hpp>
4
12
13#include <algorithm>
14#include <concepts>
15#include <cstddef>
16#include <limits>
17#include <type_traits>
18
19namespace helios::memory {
20
21/**
22 * @brief STL-compatible allocator adapter for custom allocators.
23 * @details Wraps custom allocators to work with STL containers. The underlying
24 * allocator must remain alive for the lifetime of any containers using this adapter.
25 * @note Thread-safety depends on the underlying allocator.
26 * @note The underlying allocator is held by reference - ensure it outlives all containers.
27 * @tparam T Type of objects to allocate
28 * @tparam UnderlyingAllocator The custom allocator type
29 */
30template <typename T, typename UnderlyingAllocator>
32public:
33 // Standard allocator type definitions
34 using value_type = T;
35 using size_type = size_t;
36 using difference_type = std::ptrdiff_t;
38 using is_always_equal = std::false_type;
39
40 /**
41 * @brief Rebind support for allocating different types.
42 * @details Required by STL for internal allocations (e.g., node allocators).
43 */
44 template <typename U>
48
49 /**
50 * @brief Constructs adapter with reference to underlying allocator.
51 * @param allocator Reference to the custom allocator to use
52 */
53 explicit constexpr STLAllocatorAdapter(UnderlyingAllocator& allocator) noexcept : allocator_(&allocator) {}
54
55 /**
56 * @brief Rebind copy constructor.
57 * @details Allows converting between allocators of different types.
58 */
59 template <typename U>
60 explicit constexpr STLAllocatorAdapter(const STLAllocatorAdapter<U, UnderlyingAllocator>& other) noexcept
61 : allocator_(other.allocator_) {}
62
63 constexpr STLAllocatorAdapter(const STLAllocatorAdapter&) noexcept = default;
64 constexpr STLAllocatorAdapter(STLAllocatorAdapter&&) noexcept = default;
65 constexpr ~STLAllocatorAdapter() noexcept = default;
66
67 template <typename U>
68 constexpr STLAllocatorAdapter& operator=(const STLAllocatorAdapter<U, UnderlyingAllocator>& other) noexcept;
69
70 constexpr STLAllocatorAdapter& operator=(const STLAllocatorAdapter&) noexcept = default;
71 constexpr STLAllocatorAdapter& operator=(STLAllocatorAdapter&&) noexcept = default;
72
73 /**
74 * @brief Allocates memory for n objects of type T.
75 * @throws std::bad_alloc if allocation fails
76 * @param count Count of objects to allocate space for
77 * @return Pointer to allocated memory
78 */
79 [[nodiscard]] T* allocate(size_type count);
80
81 /**
82 * @brief Deallocates memory for n objects.
83 * @param ptr Pointer to deallocate
84 * @param count Count of objects (may be ignored by some allocators)
85 */
86 void deallocate(T* ptr, size_type count) noexcept;
87
88 /**
89 * @brief Equality comparison.
90 * @details Two adapters are equal if they reference the same underlying allocator.
91 */
92 template <typename U>
93 constexpr bool operator==(const STLAllocatorAdapter<U, UnderlyingAllocator>& other) const noexcept {
94 return allocator_ == other.allocator_;
95 }
96
97 /**
98 * @brief Inequality comparison.
99 */
100 template <typename U>
101 constexpr bool operator!=(const STLAllocatorAdapter<U, UnderlyingAllocator>& other) const noexcept {
102 return !(*this == other);
103 }
104
105 /**
106 * @brief Returns maximum number of objects that can be allocated.
107 * @return Maximum size
108 */
109 [[nodiscard]] constexpr size_type max_size() const noexcept {
110 return std::numeric_limits<size_type>::max() / sizeof(T);
111 }
112
113 /**
114 * @brief Gets pointer to underlying allocator.
115 * @return Pointer to underlying allocator
116 */
117 [[nodiscard]] constexpr UnderlyingAllocator* get_allocator() const noexcept { return allocator_; }
118
119private:
120 template <typename U, typename A>
122
123 UnderlyingAllocator* allocator_ = nullptr;
124};
125
126template <typename T, typename UnderlyingAllocator>
127template <typename U>
130 allocator_ = other.allocator_;
131 return *this;
132}
133
134template <typename T, typename UnderlyingAllocator>
136 if (count > max_size()) {
137 throw std::bad_alloc();
138 }
139
140 const size_t size = count * sizeof(T);
141 constexpr size_t alignment = std::max(alignof(T), kMinAlignment);
142
143 auto result = allocator_->Allocate(size, alignment);
144
145 if (result.ptr == nullptr) {
146 throw std::bad_alloc();
147 }
148
149 return static_cast<T*>(result.ptr);
150}
151
152template <typename T, typename UnderlyingAllocator>
154 if (ptr == nullptr) [[unlikely]] {
155 return;
156 }
157
158 // FrameAllocator, DoubleFrameAllocator, and NFrameAllocator don't support individual deallocation
159 if constexpr (std::same_as<UnderlyingAllocator, FrameAllocator> ||
160 std::same_as<UnderlyingAllocator, DoubleFrameAllocator>) {
161 // No-op: Frame allocators don't support individual deallocation
162 }
163 // PoolAllocator and FreeListAllocator only need ptr
164 else if constexpr (std::same_as<UnderlyingAllocator, PoolAllocator> ||
165 std::same_as<UnderlyingAllocator, FreeListAllocator>) {
166 allocator_->Deallocate(ptr);
167 }
168 // StackAllocator needs ptr and size
169 else {
170 allocator_->Deallocate(ptr, count * sizeof(T));
171 }
172}
173
174/**
175 * @brief STL adapter for FrameAllocator.
176 * @note Deallocation is a no-op. Use Reset() on the allocator to free all memory.
177 * @warning Do not use with containers that frequently deallocate individual elements.
178 * Best for temporary containers that live for one frame.
179 * @tparam T Type of objects to allocate
180 *
181 * @example
182 * @code
183 * FrameAllocator frame_alloc(1024 * 1024);
184 * std::vector<int, STLFrameAllocator<int>> vec{STLFrameAllocator<int>(frame_alloc)};
185 * @endcode
186 */
187template <typename T>
189
190/**
191 * @brief STL adapter for PoolAllocator.
192 * @note Only suitable if sizeof(T) <= block_size of the pool.
193 * @warning May throw if T is larger than the pool's block size.
194 * @tparam T Type of objects to allocate
195 *
196 * @example
197 * @code
198 * PoolAllocator pool_alloc(sizeof(int), 100);
199 * std::vector<int, STLPoolAllocator<int>> vec{STLPoolAllocator<int>(pool_alloc)};
200 * @endcode
201 */
202template <typename T>
204
205/**
206 * @brief STL adapter for StackAllocator.
207 * @note Deallocations should follow LIFO order for optimal behavior.
208 * @warning Violating LIFO order will trigger assertions in debug builds.
209 * @tparam T Type of objects to allocate
210 *
211 * @example
212 * @code
213 * StackAllocator stack_alloc(1024 * 1024);
214 * std::vector<int, STLStackAllocator<int>> vec{STLStackAllocator<int>(stack_alloc)};
215 * @endcode
216 */
217template <typename T>
219
220/**
221 * @brief STL adapter for FreeListAllocator.
222 * @details General-purpose adapter.
223 * @note Deallocations can occur in any order.
224 * @tparam T Type of objects to allocate
225 *
226 * @example
227 * @code
228 * FreeListAllocator freelist_alloc(1024 * 1024);
229 * std::vector<int, STLFreeListAllocator<int>> vec{STLFreeListAllocator<int>(freelist_alloc)};
230 * @endcode
231 */
232template <typename T>
234
235/**
236 * @brief STL adapter for GrowableAllocator.
237 * @details Provides automatic growth for STL containers. The wrapped allocator
238 * must be compatible with GrowableAllocator (FrameAllocator, StackAllocator, FreeListAllocator).
239 * @note Deallocations are supported depending on the underlying allocator.
240 * @tparam T Type of objects to allocate
241 * @tparam UnderlyingAlloc The base allocator type to wrap with GrowableAllocator
242 *
243 * @example
244 * @code
245 * GrowableAllocator<FrameAllocator> growable_alloc(1024);
246 * std::vector<int, STLGrowableAllocator<int, FrameAllocator>> vec{
247 * STLGrowableAllocator<int, FrameAllocator>(growable_alloc)};
248 * @endcode
249 */
250template <typename T, typename UnderlyingAlloc>
252
253} // namespace helios::memory
STL-compatible allocator adapter for custom allocators.
constexpr STLAllocatorAdapter(const STLAllocatorAdapter &) noexcept=default
constexpr STLAllocatorAdapter & operator=(const STLAllocatorAdapter< U, UnderlyingAllocator > &other) noexcept
T * allocate(size_type count)
Allocates memory for n objects of type T.
constexpr size_type max_size() const noexcept
Returns maximum number of objects that can be allocated.
constexpr UnderlyingAllocator * get_allocator() const noexcept
Gets pointer to underlying allocator.
constexpr STLAllocatorAdapter(const STLAllocatorAdapter< U, UnderlyingAllocator > &other) noexcept
Rebind copy constructor.
constexpr STLAllocatorAdapter(STLAllocatorAdapter &&) noexcept=default
constexpr STLAllocatorAdapter(UnderlyingAllocator &allocator) noexcept
Constructs adapter with reference to underlying allocator.
void deallocate(T *ptr, size_type count) noexcept
Deallocates memory for n objects.
constexpr bool operator!=(const STLAllocatorAdapter< U, UnderlyingAllocator > &other) const noexcept
Inequality comparison.
constexpr size_t kMinAlignment
Minimum alignment for any allocation.
Rebind support for allocating different types.