src/zserio/pmr/PolymorphicAllocator.h
Line | Count | Source |
1 | | #ifndef ZSERIO_PMR_POLYMORPHIC_ALLOCATOR_H_INC |
2 | | #define ZSERIO_PMR_POLYMORPHIC_ALLOCATOR_H_INC |
3 | | |
4 | | #include <cstddef> |
5 | | #include <limits> |
6 | | #include <type_traits> |
7 | | #include <utility> |
8 | | |
9 | | #include "zserio/Types.h" |
10 | | #include "zserio/pmr/MemoryResource.h" |
11 | | |
12 | | namespace zserio |
13 | | { |
14 | | namespace pmr |
15 | | { |
16 | | namespace detail |
17 | | { |
18 | | |
19 | | /** |
20 | | * Polymorphic allocator inspired by C++17 standard. |
21 | | */ |
22 | | template <class T = uint8_t> |
23 | | class PolymorphicAllocatorBase |
24 | | { |
25 | | public: |
26 | | using value_type = T; |
27 | | |
28 | | // Following typedefs are only present for compatibility with older std. libraries, that does not fully |
29 | | // conform to C++11. [old-compiler-support] |
30 | | using pointer = value_type*; |
31 | | using const_pointer = const value_type*; |
32 | | using size_type = std::size_t; |
33 | | using difference_type = std::ptrdiff_t; |
34 | | using reference = value_type&; |
35 | | using const_reference = const value_type&; |
36 | | |
37 | | /** |
38 | | * Constructor. |
39 | | * |
40 | | * Note that this is intentionally non-explicit to allow to pass MemoryResource wherever |
41 | | * the PolymorphicAllocator is required. |
42 | | * |
43 | | * \param resource Memory resource. According to the C++ standard the resource may not be NULL. Since it |
44 | | * implies undefined behaviour, we define a non-standard extension here. When the resource |
45 | | * is NULL, getDefaultResource() is used instead! |
46 | | */ |
47 | | PolymorphicAllocatorBase(MemoryResource* resource = getDefaultResource()) noexcept : |
48 | | m_resource(resource != nullptr ? resource : getDefaultResource()) // non-standard extension |
49 | 142 | {} |
50 | | |
51 | | /** |
52 | | * Method generated by default. |
53 | | * \{ |
54 | | */ |
55 | | ~PolymorphicAllocatorBase() = default; |
56 | | |
57 | | PolymorphicAllocatorBase(const PolymorphicAllocatorBase& other) noexcept = default; |
58 | | PolymorphicAllocatorBase& operator=(const PolymorphicAllocatorBase& other) noexcept = default; |
59 | | |
60 | | PolymorphicAllocatorBase(PolymorphicAllocatorBase&& other) noexcept = default; |
61 | | PolymorphicAllocatorBase& operator=(PolymorphicAllocatorBase&& other) noexcept = default; |
62 | | /** |
63 | | * \} |
64 | | */ |
65 | | |
66 | | /** |
67 | | * Copy constructor from PolymorphicAllocator with another value_type. |
68 | | * |
69 | | * \param other Other PolymorphicAllocator. |
70 | | */ |
71 | | template <class U> |
72 | | PolymorphicAllocatorBase(const PolymorphicAllocatorBase<U>& other) noexcept : |
73 | | m_resource(other.resource()) |
74 | 402 | {} |
75 | | |
76 | | /** |
77 | | * Assignment operator from PolymorphicAllocator with another value_type. |
78 | | * |
79 | | * \param other Other PolymorphicAllocator. |
80 | | */ |
81 | | template <class U> |
82 | | PolymorphicAllocatorBase& operator=(const PolymorphicAllocatorBase<U>& other) noexcept |
83 | | { |
84 | | m_resource = other.resource(); |
85 | | return *this; |
86 | | } |
87 | | |
88 | | /** |
89 | | * Allocates memory for n values. |
90 | | * |
91 | | * \param size Number of values to allocate memory for. |
92 | | */ |
93 | | value_type* allocate(std::size_t size) |
94 | 196 | { |
95 | 196 | return static_cast<value_type*>(m_resource->allocate(size * sizeof(value_type), alignof(value_type))); |
96 | 196 | } |
97 | | |
98 | | /** |
99 | | * Deallocates memory for n values. |
100 | | * |
101 | | * \param memory Pointer to the memory to deallocate. |
102 | | * \param size Number of values held by the memory pointed to by memory. |
103 | | * Shall be the same size as was used for allocation of memory. |
104 | | */ |
105 | | void deallocate(value_type* memory, std::size_t size) noexcept |
106 | 196 | { |
107 | 196 | m_resource->deallocate(memory, size * sizeof(value_type), alignof(value_type)); |
108 | 196 | } |
109 | | |
110 | | /** |
111 | | * Gets the underlying memory resource. |
112 | | */ |
113 | | MemoryResource* resource() const noexcept |
114 | 554 | { |
115 | 554 | return m_resource; |
116 | 554 | } |
117 | | |
118 | | /** |
119 | | * Constructs an object in allocated memory. |
120 | | * |
121 | | * \param ptr Pointer to memory where the object will be constructed. |
122 | | * \param args Parameters to be forwarded to the object constructor. |
123 | | * \note This is only necessary for compatibility with older std. libraries, that does not fully |
124 | | * conform to C++11. [old-compiler-support] |
125 | | */ |
126 | | template <typename U, typename... Args> |
127 | | void construct(U* ptr, Args&&... args) noexcept( |
128 | | noexcept(new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...))) |
129 | 214 | { |
130 | 214 | new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...); |
131 | 214 | } |
132 | | |
133 | | /** |
134 | | * Destroys an object |
135 | | * |
136 | | * \param ptr Pointer to the object to be destroyed. |
137 | | * \note This is only necessary for compatibility with older std. libraries, that does not fully |
138 | | * conform to C++11. [old-compiler-support] |
139 | | */ |
140 | | template <typename U> |
141 | | void destroy(U* ptr) noexcept(noexcept(ptr->~U())) |
142 | 214 | { |
143 | 214 | ptr->~U(); |
144 | 214 | } |
145 | | |
146 | | /** |
147 | | * Returns theoretical maximal limit for allocation. |
148 | | * |
149 | | * \return Theoretical maximal limit for allocation. |
150 | | * \note This is only necessary for compatibility with older std. libraries, that does not fully |
151 | | * conform to C++11. [old-compiler-support] |
152 | | */ |
153 | | size_type max_size() const noexcept |
154 | 129 | { |
155 | 129 | return std::numeric_limits<size_type>::max() / sizeof(value_type); |
156 | 129 | } |
157 | | |
158 | | private: |
159 | | MemoryResource* m_resource; |
160 | | }; |
161 | | |
162 | | template <class T, class U> |
163 | | bool operator==(const PolymorphicAllocatorBase<T>& lhs, const PolymorphicAllocatorBase<U>& rhs) noexcept |
164 | 68 | { |
165 | 68 | return *lhs.resource() == *rhs.resource(); |
166 | 68 | } |
167 | | |
168 | | template <class T, class U> |
169 | | bool operator!=(const PolymorphicAllocatorBase<T>& lhs, const PolymorphicAllocatorBase<U>& rhs) noexcept |
170 | 6 | { |
171 | 6 | return !(lhs == rhs); |
172 | 6 | } |
173 | | |
174 | | } // namespace detail |
175 | | |
176 | | /** |
177 | | * Non-propagating version of the polymorphic allocator. This allocator behaves as the polymorphic_allocator |
178 | | * from C++17. |
179 | | */ |
180 | | template <class T = uint8_t> |
181 | | class PolymorphicAllocator : public detail::PolymorphicAllocatorBase<T> |
182 | | { |
183 | | public: |
184 | | using detail::PolymorphicAllocatorBase<T>::PolymorphicAllocatorBase; |
185 | | |
186 | | using propagate_on_container_copy_assignment = std::false_type; |
187 | | using propagate_on_container_move_assignment = std::false_type; |
188 | | using propagate_on_container_swap = std::false_type; |
189 | | |
190 | | /** |
191 | | * Returns instance of the allocator to be used when a container gets copied. |
192 | | */ |
193 | | PolymorphicAllocator select_on_container_copy_construction() const |
194 | 1 | { |
195 | 1 | return PolymorphicAllocator(); |
196 | 1 | } |
197 | | |
198 | | /** |
199 | | * Rebind template. |
200 | | * \note This is only necessary for compatibility with older std. libraries, that does not fully |
201 | | * conform to C++11. [old-compiler-support] |
202 | | */ |
203 | | template <typename U> |
204 | | struct rebind |
205 | | { |
206 | | using other = PolymorphicAllocator<U>; |
207 | | }; |
208 | | }; |
209 | | |
210 | | /** |
211 | | * Propagating version of the polymorphic allocator. This one is propagated on container copy and assignment. |
212 | | */ |
213 | | template <class T = uint8_t> |
214 | | class PropagatingPolymorphicAllocator : public detail::PolymorphicAllocatorBase<T> |
215 | | { |
216 | | public: |
217 | | using detail::PolymorphicAllocatorBase<T>::PolymorphicAllocatorBase; |
218 | | |
219 | | using propagate_on_container_copy_assignment = std::true_type; |
220 | | using propagate_on_container_move_assignment = std::true_type; |
221 | | using propagate_on_container_swap = std::true_type; |
222 | | |
223 | | /** |
224 | | * Returns instance of the allocator to be used when a container gets copied. |
225 | | */ |
226 | | PropagatingPolymorphicAllocator select_on_container_copy_construction() const |
227 | 29 | { |
228 | 29 | return *this; |
229 | 29 | } |
230 | | |
231 | | /** |
232 | | * Rebind template. |
233 | | * \note This is only necessary for compatibility with older std. libraries, that does not fully |
234 | | * conform to C++11. [old-compiler-support] |
235 | | */ |
236 | | template <typename U> |
237 | | struct rebind |
238 | | { |
239 | | using other = PropagatingPolymorphicAllocator<U>; |
240 | | }; |
241 | | }; |
242 | | |
243 | | } // namespace pmr |
244 | | } // namespace zserio |
245 | | |
246 | | #endif // ZSERIO_PMR_POLYMORPHIC_ALLOCATOR_H_INC |