Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
allocator_traits.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6#include <concepts>
7#include <cstddef>
8#include <expected>
9#include <memory_resource>
10#include <span>
11
12namespace helios::mem {
13
14/**
15 * @brief Concept for PMR resources used by the memory module.
16 * @tparam T Candidate type
17 */
18template <typename T>
19concept PmrAllocator = std::derived_from<T, std::pmr::memory_resource>;
20
21/**
22 * @brief Concept for PMR resources that expose allocator statistics.
23 * @tparam T Candidate type
24 */
25template <typename T>
27 PmrAllocator<T> && requires(const T& allocator) {
28 { allocator.Stats() } -> std::same_as<AllocatorStats>;
29 };
30
31/**
32 * @brief Concept for PMR resources that support reset.
33 * @tparam T Candidate type
34 */
35template <typename T>
36concept ResettablePmrAllocator = PmrAllocator<T> && requires(T& allocator) {
37 { allocator.Reset() } -> std::same_as<void>;
38};
39
40/**
41 * @brief Tries to allocate one object from a PMR allocator.
42 * @tparam T Object type
43 * @tparam Alloc Allocator type
44 * @param allocator Allocator reference
45 * @return Pointer to allocated object memory, or `MemoryError`
46 */
47template <typename T, PmrAllocator Alloc>
48[[nodiscard]] inline auto TryAllocate(Alloc& allocator) noexcept
49 -> std::expected<T*, MemoryError> {
50 if constexpr (sizeof(T) == 0) {
51 return std::unexpected(MemoryError::kInvalidSize);
52 }
53
54 constexpr size_t kAlignment = std::max(alignof(T), kMinAlignment);
55 if (!IsPowerOfTwo(kAlignment)) [[unlikely]] {
56 return std::unexpected(MemoryError::kInvalidAlignment);
57 }
58
59 void* const ptr = allocator.allocate(sizeof(T), kAlignment);
60 if (ptr == nullptr) [[unlikely]] {
61 return std::unexpected(MemoryError::kOutOfMemory);
62 }
63
64 return static_cast<T*>(ptr);
65}
66
67/**
68 * @brief Tries to allocate an array of objects from a PMR allocator.
69 * @tparam T Element type
70 * @tparam Alloc Allocator type
71 * @param allocator Allocator reference
72 * @param count Number of elements
73 * @return Span over allocated memory, or `MemoryError`
74 */
75template <typename T, PmrAllocator Alloc>
76[[nodiscard]] inline auto TryAllocateSpan(Alloc& allocator,
77 size_t count) noexcept
78 -> std::expected<std::span<T>, MemoryError> {
79 if (count == 0) [[unlikely]] {
80 return std::span<T>{};
81 }
82
83 if (count > (std::numeric_limits<size_t>::max() / sizeof(T))) [[unlikely]] {
84 return std::unexpected(MemoryError::kInvalidSize);
85 }
86
87 constexpr size_t kAlignment = std::max(alignof(T), kMinAlignment);
88 if (!IsPowerOfTwo(kAlignment)) [[unlikely]] {
89 return std::unexpected(MemoryError::kInvalidAlignment);
90 }
91
92 void* const ptr = allocator.allocate(sizeof(T) * count, kAlignment);
93 if (ptr == nullptr) [[unlikely]] {
94 return std::unexpected(MemoryError::kOutOfMemory);
95 }
96 return std::span<T>(static_cast<T*>(ptr), count);
97}
98
99/**
100 * @brief Deallocates an object allocated by `TryAllocate`.
101 * @tparam T Object type
102 * @tparam Alloc Allocator type
103 * @param allocator Allocator reference
104 * @param ptr Pointer returned by `TryAllocate`
105 */
106template <typename T, PmrAllocator Alloc>
107inline void Deallocate(Alloc& allocator, T* ptr) noexcept {
108 if (ptr == nullptr) [[unlikely]] {
109 return;
110 }
111
112 constexpr size_t kAlignment = std::max(alignof(T), kMinAlignment);
113 allocator.deallocate(ptr, sizeof(T), kAlignment);
114}
115
116/**
117 * @brief Deallocates an array allocated by `TryAllocateSpan`.
118 * @tparam T Element type
119 * @tparam Alloc Allocator type
120 * @param allocator Allocator reference
121 * @param span Span returned by `TryAllocateSpan`
122 */
123template <typename T, PmrAllocator Alloc>
124inline void Deallocate(Alloc& allocator, std::span<T> span) noexcept {
125 if (span.empty()) [[unlikely]] {
126 return;
127 }
128
129 constexpr size_t kAlignment = std::max(alignof(T), kMinAlignment);
130 allocator.deallocate(span.data(), sizeof(T) * span.size(), kAlignment);
131}
132
133/**
134 * @brief Adapts stats-aware PMR resources.
135 * @tparam Alloc Allocator type
136 * @param allocator Allocator reference
137 * @return Stats if available, otherwise zero-initialized stats
138 */
139template <PmrAllocator Alloc>
140[[nodiscard]] constexpr AllocatorStats Stats(const Alloc& allocator) noexcept {
141 if constexpr (PmrAllocatorWithStats<Alloc>) {
142 return allocator.Stats();
143 } else {
144 return {};
145 }
146}
147
148} // namespace helios::mem
Concept for PMR resources that expose allocator statistics.
Concept for PMR resources used by the memory module.
Concept for PMR resources that support reset.
constexpr bool IsPowerOfTwo(size_t value) noexcept
Checks if a value is a power of two.
Definition common.hpp:215
constexpr size_t kMinAlignment
Minimum alignment used by memory resources.
Definition common.hpp:17
void Deallocate(Alloc &allocator, T *ptr) noexcept
Deallocates an object allocated by TryAllocate.
auto TryAllocate(Alloc &allocator) noexcept -> std::expected< T *, MemoryError >
Tries to allocate one object from a PMR allocator.
auto TryAllocateSpan(Alloc &allocator, size_t count) noexcept -> std::expected< std::span< T >, MemoryError >
Tries to allocate an array of objects from a PMR allocator.
MemoryError
Error codes used for explicit non-fatal memory operations.
Definition common.hpp:179
@ kInvalidAlignment
Requested alignment is not supported.
Definition common.hpp:181
@ kOutOfMemory
Allocation request could not be satisfied.
Definition common.hpp:180
@ kInvalidSize
Requested size is zero or overflows.
Definition common.hpp:182
constexpr AllocatorStats Stats(const Alloc &allocator) noexcept
Adapts stats-aware PMR resources.
Runtime statistics snapshot for PMR allocators.
Definition common.hpp:163