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