Line | Count | Source |
1 | | #ifndef ZSERIO_ANY_HOLDER_H_INC |
2 | | #define ZSERIO_ANY_HOLDER_H_INC |
3 | | |
4 | | #include <cstddef> |
5 | | #include <type_traits> |
6 | | |
7 | | #include "zserio/AllocatorHolder.h" |
8 | | #include "zserio/CppRuntimeException.h" |
9 | | #include "zserio/NoInit.h" |
10 | | #include "zserio/OptionalHolder.h" |
11 | | #include "zserio/RebindAlloc.h" |
12 | | #include "zserio/Traits.h" |
13 | | #include "zserio/Types.h" |
14 | | |
15 | | namespace zserio |
16 | | { |
17 | | |
18 | | namespace detail |
19 | | { |
20 | | |
21 | | class TypeIdHolder |
22 | | { |
23 | | public: |
24 | | using type_id = const int*; |
25 | | |
26 | | template <typename T> |
27 | | static type_id get() |
28 | 150k | { |
29 | 150k | static int currentTypeId; |
30 | | |
31 | 150k | return ¤tTypeId; |
32 | 150k | } |
33 | | }; |
34 | | |
35 | | // Interface for object holders |
36 | | template <typename ALLOC> |
37 | | class IHolder |
38 | | { |
39 | | public: |
40 | 66.3k | virtual ~IHolder() = default; |
41 | | virtual IHolder* clone(const ALLOC& allocator) const = 0; |
42 | | virtual IHolder* clone(NoInitT, const ALLOC& allocator) const = 0; |
43 | | virtual IHolder* clone(void* storage) const = 0; |
44 | | virtual IHolder* clone(NoInitT, void* storage) const = 0; |
45 | | virtual IHolder* move(const ALLOC& allocator) = 0; |
46 | | virtual IHolder* move(NoInitT, const ALLOC& allocator) = 0; |
47 | | virtual IHolder* move(void* storage) = 0; |
48 | | virtual IHolder* move(NoInitT, void* storage) = 0; |
49 | | virtual void destroy(const ALLOC& allocator) = 0; |
50 | | virtual bool isType(detail::TypeIdHolder::type_id typeId) const = 0; |
51 | | }; |
52 | | |
53 | | // Base of object holders, holds a value in the inplace_optional_holder |
54 | | template <typename T, typename ALLOC> |
55 | | class HolderBase : public IHolder<ALLOC> |
56 | | { |
57 | | public: |
58 | | template <typename U> |
59 | | void set(U&& value) |
60 | 50.1k | { |
61 | 50.1k | m_typedHolder = std::forward<U>(value); |
62 | 50.1k | } |
63 | | |
64 | | template <typename U, typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
65 | | void set(NoInitT, U&& value) |
66 | 4 | { |
67 | | // inplace_optional_holder constructor to prevent it's implicit constructor |
68 | 4 | m_typedHolder.assign(NoInit, inplace_optional_holder<T>(NoInit, std::forward<U>(value))); |
69 | 4 | } |
70 | | |
71 | | template <typename U, typename std::enable_if<!std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
72 | | void set(NoInitT, U&& value) |
73 | 14 | { |
74 | 14 | m_typedHolder = std::forward<U>(value); |
75 | 14 | } |
76 | | |
77 | | void setHolder(const inplace_optional_holder<T>& value) |
78 | 29 | { |
79 | 29 | m_typedHolder = value; |
80 | 29 | } |
81 | | |
82 | | void setHolder(inplace_optional_holder<T>&& value) |
83 | 16.2k | { |
84 | 16.2k | m_typedHolder = std::move(value); |
85 | 16.2k | } |
86 | | |
87 | | template <typename U, typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
88 | | void setHolder(NoInitT, const inplace_optional_holder<U>& value) |
89 | 6 | { |
90 | 6 | m_typedHolder.assign(NoInit, value); |
91 | 6 | } |
92 | | |
93 | | template <typename U, typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
94 | | void setHolder(NoInitT, inplace_optional_holder<U>&& value) |
95 | 3 | { |
96 | 3 | m_typedHolder.assign(NoInit, std::move(value)); |
97 | 3 | } |
98 | | |
99 | | template <typename U, typename std::enable_if<!std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
100 | | void setHolder(NoInitT, const inplace_optional_holder<U>& value) |
101 | 19 | { |
102 | 19 | m_typedHolder = value; |
103 | 19 | } |
104 | | |
105 | | template <typename U, typename std::enable_if<!std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
106 | | void setHolder(NoInitT, inplace_optional_holder<U>&& value) |
107 | 19 | { |
108 | 19 | m_typedHolder = std::move(value); |
109 | 19 | } |
110 | | |
111 | | T& get() |
112 | 740 | { |
113 | 740 | return m_typedHolder.value(); |
114 | 740 | } |
115 | | |
116 | | const T& get() const |
117 | 48.7k | { |
118 | 48.7k | return m_typedHolder.value(); |
119 | 48.7k | } |
120 | | |
121 | | bool isType(detail::TypeIdHolder::type_id typeId) const override |
122 | 75.3k | { |
123 | 75.3k | return detail::TypeIdHolder::get<T>() == typeId; |
124 | 75.3k | } |
125 | | |
126 | | protected: |
127 | | inplace_optional_holder<T>& getHolder() |
128 | 16.3k | { |
129 | 16.3k | return m_typedHolder; |
130 | 16.3k | } |
131 | | |
132 | | const inplace_optional_holder<T>& getHolder() const |
133 | 54 | { |
134 | 54 | return m_typedHolder; |
135 | 54 | } |
136 | | |
137 | | private: |
138 | | inplace_optional_holder<T> m_typedHolder; |
139 | | }; |
140 | | |
141 | | // Holder allocated on heap |
142 | | template <typename T, typename ALLOC> |
143 | | class HeapHolder : public HolderBase<T, ALLOC> |
144 | | { |
145 | | private: |
146 | | struct ConstructTag |
147 | | {}; |
148 | | |
149 | | public: |
150 | | using this_type = HeapHolder<T, ALLOC>; |
151 | | |
152 | | explicit HeapHolder(ConstructTag) noexcept |
153 | 16.6k | {} |
154 | | |
155 | | static this_type* create(const ALLOC& allocator) |
156 | 16.6k | { |
157 | 16.6k | using AllocType = RebindAlloc<ALLOC, this_type>; |
158 | 16.6k | using AllocTraits = std::allocator_traits<AllocType>; |
159 | | |
160 | 16.6k | AllocType typedAlloc = allocator; |
161 | 16.6k | typename AllocTraits::pointer ptr = AllocTraits::allocate(typedAlloc, 1); |
162 | | // this never throws because HeapHolder constructor never throws |
163 | 16.6k | AllocTraits::construct(typedAlloc, std::addressof(*ptr), ConstructTag{}); |
164 | 16.6k | return ptr; |
165 | 16.6k | } |
166 | | |
167 | | IHolder<ALLOC>* clone(const ALLOC& allocator) const override |
168 | 12 | { |
169 | 12 | this_type* holder = create(allocator); |
170 | 12 | holder->setHolder(this->getHolder()); |
171 | 12 | return holder; |
172 | 12 | } |
173 | | |
174 | | IHolder<ALLOC>* clone(NoInitT, const ALLOC& allocator) const override |
175 | 12 | { |
176 | 12 | this_type* holder = create(allocator); |
177 | 12 | holder->setHolder(NoInit, this->getHolder()); |
178 | 12 | return holder; |
179 | 12 | } |
180 | | |
181 | | IHolder<ALLOC>* clone(void*) const override |
182 | 1 | { |
183 | 1 | throw CppRuntimeException("AnyHolder: Unexpected clone call."); |
184 | 1 | } |
185 | | |
186 | | IHolder<ALLOC>* clone(NoInitT, void*) const override |
187 | 1 | { |
188 | 1 | throw CppRuntimeException("AnyHolder: Unexpected clone call."); |
189 | 1 | } |
190 | | |
191 | | IHolder<ALLOC>* move(const ALLOC& allocator) override |
192 | 6 | { |
193 | 6 | this_type* holder = create(allocator); |
194 | 6 | holder->setHolder(std::move(this->getHolder())); |
195 | 6 | return holder; |
196 | 6 | } |
197 | | |
198 | | IHolder<ALLOC>* move(NoInitT, const ALLOC& allocator) override |
199 | 6 | { |
200 | 6 | this_type* holder = create(allocator); |
201 | 6 | holder->setHolder(NoInit, std::move(this->getHolder())); |
202 | 6 | return holder; |
203 | 6 | } |
204 | | |
205 | | IHolder<ALLOC>* move(void*) override |
206 | 1 | { |
207 | 1 | throw CppRuntimeException("AnyHolder: Unexpected move call."); |
208 | 1 | } |
209 | | |
210 | | IHolder<ALLOC>* move(NoInitT, void*) override |
211 | 1 | { |
212 | 1 | throw CppRuntimeException("AnyHolder: Unexpected move call."); |
213 | 1 | } |
214 | | |
215 | | void destroy(const ALLOC& allocator) override |
216 | 16.6k | { |
217 | 16.6k | using AllocType = RebindAlloc<ALLOC, this_type>; |
218 | 16.6k | using AllocTraits = std::allocator_traits<AllocType>; |
219 | | |
220 | 16.6k | AllocType typedAlloc = allocator; |
221 | 16.6k | AllocTraits::destroy(typedAlloc, this); |
222 | 16.6k | AllocTraits::deallocate(typedAlloc, this, 1); |
223 | 16.6k | } |
224 | | }; |
225 | | |
226 | | // Holder allocated in the in-place storage |
227 | | template <typename T, typename ALLOC> |
228 | | class NonHeapHolder : public HolderBase<T, ALLOC> |
229 | | { |
230 | | public: |
231 | | using this_type = NonHeapHolder<T, ALLOC>; |
232 | | |
233 | | static this_type* create(void* storage) |
234 | 33.3k | { |
235 | 33.3k | return new (storage) this_type(); |
236 | 33.3k | } |
237 | | |
238 | | IHolder<ALLOC>* clone(const ALLOC&) const override |
239 | 1 | { |
240 | 1 | throw CppRuntimeException("AnyHolder: Unexpected clone call."); |
241 | 1 | } |
242 | | |
243 | | IHolder<ALLOC>* clone(NoInitT, const ALLOC&) const override |
244 | 1 | { |
245 | 1 | throw CppRuntimeException("AnyHolder: Unexpected clone call."); |
246 | 1 | } |
247 | | |
248 | | IHolder<ALLOC>* clone(void* storage) const override |
249 | 17 | { |
250 | 17 | NonHeapHolder* holder = new (storage) NonHeapHolder(); |
251 | 17 | holder->setHolder(this->getHolder()); |
252 | 17 | return holder; |
253 | 17 | } |
254 | | |
255 | | IHolder<ALLOC>* clone(NoInitT, void* storage) const override |
256 | 13 | { |
257 | 13 | NonHeapHolder* holder = new (storage) NonHeapHolder(); |
258 | 13 | holder->setHolder(NoInit, this->getHolder()); |
259 | 13 | return holder; |
260 | 13 | } |
261 | | |
262 | | IHolder<ALLOC>* move(const ALLOC&) override |
263 | 1 | { |
264 | 1 | throw CppRuntimeException("AnyHolder: Unexpected move call."); |
265 | 1 | } |
266 | | |
267 | | IHolder<ALLOC>* move(NoInitT, const ALLOC&) override |
268 | 1 | { |
269 | 1 | throw CppRuntimeException("AnyHolder: Unexpected move call."); |
270 | 1 | } |
271 | | |
272 | | IHolder<ALLOC>* move(void* storage) override |
273 | 16.2k | { |
274 | 16.2k | NonHeapHolder* holder = new (storage) NonHeapHolder(); |
275 | 16.2k | holder->setHolder(std::move(this->getHolder())); |
276 | 16.2k | return holder; |
277 | 16.2k | } |
278 | | |
279 | | IHolder<ALLOC>* move(NoInitT, void* storage) override |
280 | 16 | { |
281 | 16 | NonHeapHolder* holder = new (storage) NonHeapHolder(); |
282 | 16 | holder->setHolder(NoInit, std::move(this->getHolder())); |
283 | 16 | return holder; |
284 | 16 | } |
285 | | |
286 | | void destroy(const ALLOC&) override |
287 | 49.6k | { |
288 | 49.6k | this->~NonHeapHolder(); |
289 | 49.6k | } |
290 | | |
291 | | private: |
292 | 49.6k | NonHeapHolder() = default; |
293 | | }; |
294 | | |
295 | | template <typename ALLOC> |
296 | | union UntypedHolder |
297 | | { |
298 | | // 2 * sizeof(void*) for T + sizeof(void*) for Holder's vptr |
299 | | using MaxInPlaceType = std::aligned_storage<3 * sizeof(void*), alignof(void*)>::type; |
300 | | |
301 | | detail::IHolder<ALLOC>* heap; |
302 | | MaxInPlaceType inPlace; |
303 | | }; |
304 | | |
305 | | template <typename T, typename ALLOC> |
306 | | using has_non_heap_holder = std::integral_constant<bool, |
307 | | sizeof(NonHeapHolder<T, ALLOC>) <= sizeof(typename UntypedHolder<ALLOC>::MaxInPlaceType) && |
308 | | std::is_nothrow_move_constructible<T>::value && |
309 | | alignof(T) <= alignof(typename UntypedHolder<ALLOC>::MaxInPlaceType)>; |
310 | | |
311 | | } // namespace detail |
312 | | |
313 | | /** |
314 | | * Type safe container for single values of any type which doesn't need RTTI. |
315 | | */ |
316 | | template <typename ALLOC = std::allocator<uint8_t>> |
317 | | class AnyHolder : public AllocatorHolder<ALLOC> |
318 | | { |
319 | | using AllocTraits = std::allocator_traits<ALLOC>; |
320 | | using AllocatorHolder<ALLOC>::get_allocator_ref; |
321 | | using AllocatorHolder<ALLOC>::set_allocator; |
322 | | |
323 | | public: |
324 | | using AllocatorHolder<ALLOC>::get_allocator; |
325 | | using allocator_type = ALLOC; |
326 | | |
327 | | /** |
328 | | * Empty constructor. |
329 | | */ |
330 | | AnyHolder() : |
331 | | AnyHolder(ALLOC()) |
332 | 375 | {} |
333 | | |
334 | | /** |
335 | | * Constructor from given allocator |
336 | | */ |
337 | | explicit AnyHolder(const ALLOC& allocator) : |
338 | | AllocatorHolder<ALLOC>(allocator) |
339 | 950 | { |
340 | 950 | m_untypedHolder.heap = nullptr; |
341 | 950 | } |
342 | | |
343 | | /** |
344 | | * Constructor from any value. |
345 | | * |
346 | | * \param value Value of any type to hold. Supports move semantic. |
347 | | */ |
348 | | template <typename T, |
349 | | typename std::enable_if<!std::is_same<typename std::decay<T>::type, AnyHolder>::value && |
350 | | !std::is_same<typename std::decay<T>::type, ALLOC>::value, |
351 | | int>::type = 0> |
352 | | explicit AnyHolder(T&& value, const ALLOC& allocator = ALLOC()) : |
353 | | AllocatorHolder<ALLOC>(allocator) |
354 | 25.1k | { |
355 | 25.1k | m_untypedHolder.heap = nullptr; |
356 | 25.1k | set(std::forward<T>(value)); |
357 | 25.1k | } |
358 | | |
359 | | /** |
360 | | * Constructor from any value which prevents initialization. |
361 | | * |
362 | | * \param value Value of any type to hold. Supports move semantic. |
363 | | */ |
364 | | template <typename T, |
365 | | typename std::enable_if<!std::is_same<typename std::decay<T>::type, AnyHolder>::value, int>::type = |
366 | | 0> |
367 | | explicit AnyHolder(NoInitT, T&& value, const ALLOC& allocator = ALLOC()) : |
368 | | AllocatorHolder<ALLOC>(allocator) |
369 | 18 | { |
370 | 18 | m_untypedHolder.heap = nullptr; |
371 | 18 | set(NoInit, std::forward<T>(value)); |
372 | 18 | } |
373 | | |
374 | | /** |
375 | | * Destructor. |
376 | | */ |
377 | | ~AnyHolder() |
378 | 26.3k | { |
379 | 26.3k | clearHolder(); |
380 | 26.3k | } |
381 | | |
382 | | /** |
383 | | * Copy constructor. |
384 | | * |
385 | | * \param other Any holder to copy. |
386 | | */ |
387 | | AnyHolder(const AnyHolder& other) : |
388 | | AllocatorHolder<ALLOC>( |
389 | | AllocTraits::select_on_container_copy_construction(other.get_allocator_ref())) |
390 | 29 | { |
391 | 29 | copy(other); |
392 | 29 | } |
393 | | |
394 | | /** |
395 | | * Copy constructor which prevents initialization. |
396 | | * |
397 | | * \param other Any holder to copy. |
398 | | */ |
399 | | AnyHolder(NoInitT, const AnyHolder& other) : |
400 | | AllocatorHolder<ALLOC>( |
401 | | AllocTraits::select_on_container_copy_construction(other.get_allocator_ref())) |
402 | 24 | { |
403 | 24 | copy(NoInit, other); |
404 | 24 | } |
405 | | |
406 | | /** |
407 | | * Allocator-extended copy constructor. |
408 | | * |
409 | | * \param other Any holder to copy. |
410 | | * \param allocator Allocator to be used for dynamic memory allocations. |
411 | | */ |
412 | | AnyHolder(const AnyHolder& other, const ALLOC& allocator) : |
413 | | AllocatorHolder<ALLOC>(allocator) |
414 | 8 | { |
415 | 8 | copy(other); |
416 | 8 | } |
417 | | |
418 | | /** |
419 | | * Allocator-extended copy constructor which prevents initialization. |
420 | | * |
421 | | * \param other Any holder to copy. |
422 | | * \param allocator Allocator to be used for dynamic memory allocations. |
423 | | */ |
424 | | AnyHolder(NoInitT, const AnyHolder& other, const ALLOC& allocator) : |
425 | | AllocatorHolder<ALLOC>(allocator) |
426 | 8 | { |
427 | 8 | copy(NoInit, other); |
428 | 8 | } |
429 | | |
430 | | /** |
431 | | * Copy assignment operator. |
432 | | * |
433 | | * \param other Any holder to copy. |
434 | | * |
435 | | * \return Reference to this. |
436 | | */ |
437 | | AnyHolder& operator=(const AnyHolder& other) |
438 | 24 | { |
439 | 24 | if (this != &other) |
440 | 16 | { |
441 | | // TODO: do not dealloc unless necessary |
442 | 16 | clearHolder(); |
443 | 16 | if (AllocTraits::propagate_on_container_copy_assignment::value) |
444 | 8 | set_allocator(other.get_allocator_ref()); |
445 | 16 | copy(other); |
446 | 16 | } |
447 | | |
448 | 24 | return *this; |
449 | 24 | } |
450 | | |
451 | | /** |
452 | | * Copy assignment operator which prevents initialization. |
453 | | * |
454 | | * \param other Any holder to copy. |
455 | | * |
456 | | * \return Reference to this. |
457 | | */ |
458 | | AnyHolder& assign(NoInitT, const AnyHolder& other) |
459 | 37 | { |
460 | 37 | if (this != &other) |
461 | 29 | { |
462 | | // TODO: do not dealloc unless necessary |
463 | 29 | clearHolder(); |
464 | 29 | if (AllocTraits::propagate_on_container_copy_assignment::value) |
465 | 8 | set_allocator(other.get_allocator_ref()); |
466 | 29 | copy(NoInit, other); |
467 | 29 | } |
468 | | |
469 | 37 | return *this; |
470 | 37 | } |
471 | | |
472 | | /** |
473 | | * Move constructor. |
474 | | * |
475 | | * \param other Any holder to move from. |
476 | | */ |
477 | | AnyHolder(AnyHolder&& other) noexcept : |
478 | | AllocatorHolder<ALLOC>(std::move(other.get_allocator_ref())) |
479 | 45 | { |
480 | 45 | move(std::move(other)); |
481 | 45 | } |
482 | | |
483 | | /** |
484 | | * Move constructor which prevents initialization. |
485 | | * |
486 | | * \param other Any holder to move from. |
487 | | */ |
488 | | AnyHolder(NoInitT, AnyHolder&& other) noexcept : |
489 | | AllocatorHolder<ALLOC>(std::move(other.get_allocator_ref())) |
490 | 29 | { |
491 | 29 | move(NoInit, std::move(other)); |
492 | 29 | } |
493 | | |
494 | | /** |
495 | | * Allocator-extended move constructor. |
496 | | * |
497 | | * \param other Any holder to move from. |
498 | | * \param allocator Allocator to be used for dynamic memory allocations. |
499 | | */ |
500 | | AnyHolder(AnyHolder&& other, const ALLOC& allocator) : |
501 | | AllocatorHolder<ALLOC>(allocator) |
502 | 24 | { |
503 | 24 | move(std::move(other)); |
504 | 24 | } |
505 | | |
506 | | /** |
507 | | * Allocator-extended move constructor which prevents initialization. |
508 | | * |
509 | | * \param other Any holder to move from. |
510 | | * \param allocator Allocator to be used for dynamic memory allocations. |
511 | | */ |
512 | | AnyHolder(NoInitT, AnyHolder&& other, const ALLOC& allocator) : |
513 | | AllocatorHolder<ALLOC>(allocator) |
514 | 24 | { |
515 | 24 | move(NoInit, std::move(other)); |
516 | 24 | } |
517 | | |
518 | | /** |
519 | | * Move assignment operator. |
520 | | * |
521 | | * \param other Any holder to move from. |
522 | | * |
523 | | * \return Reference to this. |
524 | | */ |
525 | | AnyHolder& operator=(AnyHolder&& other) |
526 | 48.9k | { |
527 | 48.9k | if (this != &other) |
528 | 48.9k | { |
529 | 48.9k | clearHolder(); |
530 | 48.9k | if (AllocTraits::propagate_on_container_move_assignment::value) |
531 | 48.9k | set_allocator(std::move(other.get_allocator_ref())); |
532 | 48.9k | move(std::move(other)); |
533 | 48.9k | } |
534 | | |
535 | 48.9k | return *this; |
536 | 48.9k | } |
537 | | |
538 | | /** |
539 | | * Move assignment operator which prevents initialization. |
540 | | * |
541 | | * \param other Any holder to move from. |
542 | | * |
543 | | * \return Reference to this. |
544 | | */ |
545 | | AnyHolder& assign(NoInitT, AnyHolder&& other) |
546 | 27 | { |
547 | 27 | if (this != &other) |
548 | 19 | { |
549 | 19 | clearHolder(); |
550 | 19 | if (AllocTraits::propagate_on_container_move_assignment::value) |
551 | 11 | set_allocator(std::move(other.get_allocator_ref())); |
552 | 19 | move(NoInit, std::move(other)); |
553 | 19 | } |
554 | | |
555 | 27 | return *this; |
556 | 27 | } |
557 | | |
558 | | /** |
559 | | * Value assignment operator. |
560 | | * |
561 | | * \param value Any value to assign. Supports move semantic. |
562 | | * |
563 | | * \return Reference to this. |
564 | | */ |
565 | | template <typename T, |
566 | | typename std::enable_if<!std::is_same<typename std::decay<T>::type, AnyHolder>::value, int>::type = |
567 | | 0> |
568 | | AnyHolder& operator=(T&& value) |
569 | 72 | { |
570 | 72 | set(std::forward<T>(value)); |
571 | | |
572 | 72 | return *this; |
573 | 72 | } |
574 | | |
575 | | /** |
576 | | * Resets the holder. |
577 | | */ |
578 | | void reset() |
579 | 68 | { |
580 | 68 | clearHolder(); |
581 | 68 | } |
582 | | |
583 | | /** |
584 | | * Sets any value to the holder. |
585 | | * |
586 | | * \param value Any value to set. Supports move semantic. |
587 | | */ |
588 | | template <typename T> |
589 | | void set(T&& value) |
590 | 50.1k | { |
591 | 50.1k | createHolder<typename std::decay<T>::type>()->set(std::forward<T>(value)); |
592 | 50.1k | } |
593 | | |
594 | | /** |
595 | | * Sets any value to the holder and prevent initialization. |
596 | | * |
597 | | * \param value Any value to set. Supports move semantic. |
598 | | */ |
599 | | template <typename T> |
600 | | void set(NoInitT, T&& value) |
601 | 18 | { |
602 | 18 | createHolder<typename std::decay<T>::type>()->set(NoInit, std::forward<T>(value)); |
603 | 18 | } |
604 | | |
605 | | /** |
606 | | * Gets value of the given type. |
607 | | * |
608 | | * \return Reference to value of the requested type if the type match to the stored value. |
609 | | * |
610 | | * \throw CppRuntimeException if the requested type doesn't match to the stored value. |
611 | | */ |
612 | | template <typename T> |
613 | | T& get() |
614 | 772 | { |
615 | 772 | checkType<T>(); |
616 | 772 | return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get(); |
617 | 772 | } |
618 | | |
619 | | /** |
620 | | * Gets value of the given type. |
621 | | * |
622 | | * \return Value of the requested type if the type match to the stored value. |
623 | | * |
624 | | * \throw CppRuntimeException if the requested type doesn't match to the stored value. |
625 | | */ |
626 | | template <typename T> |
627 | | const T& get() const |
628 | 48.7k | { |
629 | 48.7k | checkType<T>(); |
630 | 48.7k | return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get(); |
631 | 48.7k | } |
632 | | |
633 | | /** |
634 | | * Check whether the holder holds the given type. |
635 | | * |
636 | | * \return True if the stored value is of the given type, false otherwise. |
637 | | */ |
638 | | template <typename T> |
639 | | bool isType() const |
640 | 50.6k | { |
641 | 50.6k | return hasHolder() && getUntypedHolder()->isType(detail::TypeIdHolder::get<T>())50.6k ; |
642 | 50.6k | } |
643 | | |
644 | | /** |
645 | | * Checks whether the holder has any value. |
646 | | * |
647 | | * \return True if the holder has assigned any value, false otherwise. |
648 | | */ |
649 | | bool hasValue() const |
650 | 24.7k | { |
651 | 24.7k | return hasHolder(); |
652 | 24.7k | } |
653 | | |
654 | | private: |
655 | | void copy(const AnyHolder& other) |
656 | 53 | { |
657 | 53 | if (other.m_isInPlace) |
658 | 17 | { |
659 | 17 | other.getUntypedHolder()->clone(&m_untypedHolder.inPlace); |
660 | 17 | m_isInPlace = true; |
661 | 17 | } |
662 | 36 | else if (other.m_untypedHolder.heap != nullptr) |
663 | 12 | { |
664 | 12 | m_untypedHolder.heap = other.getUntypedHolder()->clone(get_allocator_ref()); |
665 | 12 | } |
666 | 24 | else |
667 | 24 | { |
668 | 24 | m_untypedHolder.heap = nullptr; |
669 | 24 | } |
670 | 53 | } |
671 | | |
672 | | void copy(NoInitT, const AnyHolder& other) |
673 | 61 | { |
674 | 61 | if (other.m_isInPlace) |
675 | 13 | { |
676 | 13 | other.getUntypedHolder()->clone(NoInit, &m_untypedHolder.inPlace); |
677 | 13 | m_isInPlace = true; |
678 | 13 | } |
679 | 48 | else if (other.m_untypedHolder.heap != nullptr) |
680 | 12 | { |
681 | 12 | m_untypedHolder.heap = other.getUntypedHolder()->clone(NoInit, get_allocator_ref()); |
682 | 12 | } |
683 | 36 | else |
684 | 36 | { |
685 | 36 | m_untypedHolder.heap = nullptr; |
686 | 36 | } |
687 | 61 | } |
688 | | |
689 | | void move(AnyHolder&& other) |
690 | 49.0k | { |
691 | 49.0k | if (other.m_isInPlace) |
692 | 16.2k | { |
693 | 16.2k | other.getUntypedHolder()->move(&m_untypedHolder.inPlace); |
694 | 16.2k | m_isInPlace = true; |
695 | 16.2k | other.clearHolder(); |
696 | 16.2k | } |
697 | 32.7k | else if (other.m_untypedHolder.heap != nullptr) |
698 | 32.6k | { |
699 | 32.6k | if (get_allocator_ref() == other.get_allocator_ref()) |
700 | 32.6k | { |
701 | | // take over the other's storage |
702 | 32.6k | m_untypedHolder.heap = other.m_untypedHolder.heap; |
703 | 32.6k | other.m_untypedHolder.heap = nullptr; |
704 | 32.6k | } |
705 | 6 | else |
706 | 6 | { |
707 | | // cannot steal the storage, allocate our own and move the holder |
708 | 6 | m_untypedHolder.heap = other.getUntypedHolder()->move(get_allocator_ref()); |
709 | 6 | other.clearHolder(); |
710 | 6 | } |
711 | 32.6k | } |
712 | 36 | else |
713 | 36 | { |
714 | 36 | m_untypedHolder.heap = nullptr; |
715 | 36 | } |
716 | 49.0k | } |
717 | | |
718 | | void move(NoInitT, AnyHolder&& other) |
719 | 72 | { |
720 | 72 | if (other.m_isInPlace) |
721 | 16 | { |
722 | 16 | other.getUntypedHolder()->move(NoInit, &m_untypedHolder.inPlace); |
723 | 16 | m_isInPlace = true; |
724 | 16 | other.clearHolder(); |
725 | 16 | } |
726 | 56 | else if (other.m_untypedHolder.heap != nullptr) |
727 | 17 | { |
728 | 17 | if (get_allocator_ref() == other.get_allocator_ref()) |
729 | 11 | { |
730 | | // take over the other's storage |
731 | 11 | m_untypedHolder.heap = other.m_untypedHolder.heap; |
732 | 11 | other.m_untypedHolder.heap = nullptr; |
733 | 11 | } |
734 | 6 | else |
735 | 6 | { |
736 | | // cannot steal the storage, allocate our own and move the holder |
737 | 6 | m_untypedHolder.heap = other.getUntypedHolder()->move(NoInit, get_allocator_ref()); |
738 | 6 | other.clearHolder(); |
739 | 6 | } |
740 | 17 | } |
741 | 39 | else |
742 | 39 | { |
743 | 39 | m_untypedHolder.heap = nullptr; |
744 | 39 | } |
745 | 72 | } |
746 | | |
747 | | void clearHolder() |
748 | 116k | { |
749 | 116k | if (hasHolder()) |
750 | 66.3k | { |
751 | 66.3k | getUntypedHolder()->destroy(get_allocator_ref()); |
752 | 66.3k | m_isInPlace = false; |
753 | 66.3k | m_untypedHolder.heap = nullptr; |
754 | 66.3k | } |
755 | 116k | } |
756 | | |
757 | | bool hasHolder() const |
758 | 241k | { |
759 | 241k | return (m_isInPlace || m_untypedHolder.heap != nullptr142k ); |
760 | 241k | } |
761 | | |
762 | | template <typename T> |
763 | | detail::HolderBase<T, ALLOC>* createHolder() |
764 | 50.1k | { |
765 | 50.1k | if (hasHolder()) |
766 | 24.6k | { |
767 | 24.6k | if (getUntypedHolder()->isType(detail::TypeIdHolder::get<T>())) |
768 | 211 | return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>()); |
769 | | |
770 | 24.4k | clearHolder(); |
771 | 24.4k | } |
772 | | |
773 | 49.9k | return createHolderImpl<T>(detail::has_non_heap_holder<T, ALLOC>()); |
774 | 50.1k | } |
775 | | |
776 | | template <typename T> |
777 | | detail::HolderBase<T, ALLOC>* createHolderImpl(std::true_type) |
778 | 33.3k | { |
779 | 33.3k | detail::NonHeapHolder<T, ALLOC>* holder = |
780 | 33.3k | detail::NonHeapHolder<T, ALLOC>::create(&m_untypedHolder.inPlace); |
781 | 33.3k | m_isInPlace = true; |
782 | 33.3k | return holder; |
783 | 33.3k | } |
784 | | |
785 | | template <typename T> |
786 | | detail::HolderBase<T, ALLOC>* createHolderImpl(std::false_type) |
787 | 16.6k | { |
788 | 16.6k | detail::HeapHolder<T, ALLOC>* holder = detail::HeapHolder<T, ALLOC>::create(get_allocator_ref()); |
789 | 16.6k | m_untypedHolder.heap = holder; |
790 | 16.6k | return holder; |
791 | 16.6k | } |
792 | | |
793 | | template <typename T> |
794 | | void checkType() const |
795 | 49.4k | { |
796 | 49.4k | if (!isType<T>()) |
797 | 33 | throwBadType(); |
798 | 49.4k | } |
799 | | |
800 | | /** Optimization which increases changes to inline checkType(). */ |
801 | | void throwBadType() const |
802 | 33 | { |
803 | 33 | throw CppRuntimeException("Bad type in AnyHolder"); |
804 | 33 | } |
805 | | |
806 | | template <typename T> |
807 | | detail::HeapHolder<T, ALLOC>* getHeapHolder() |
808 | 168 | { |
809 | 168 | return static_cast<detail::HeapHolder<T, ALLOC>*>(m_untypedHolder.heap); |
810 | 168 | } |
811 | | |
812 | | template <typename T> |
813 | | const detail::HeapHolder<T, ALLOC>* getHeapHolder() const |
814 | 16.4k | { |
815 | 16.4k | return static_cast<detail::HeapHolder<T, ALLOC>*>(m_untypedHolder.heap); |
816 | 16.4k | } |
817 | | |
818 | | template <typename T> |
819 | | detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() |
820 | 783 | { |
821 | 783 | return reinterpret_cast<detail::NonHeapHolder<T, ALLOC>*>(&m_untypedHolder.inPlace); |
822 | 783 | } |
823 | | |
824 | | template <typename T> |
825 | | const detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() const |
826 | 32.2k | { |
827 | 32.2k | return reinterpret_cast<const detail::NonHeapHolder<T, ALLOC>*>(&m_untypedHolder.inPlace); |
828 | 32.2k | } |
829 | | |
830 | | template <typename T> |
831 | | detail::HolderBase<T, ALLOC>* getHolder(std::true_type) |
832 | 783 | { |
833 | 783 | return static_cast<detail::HolderBase<T, ALLOC>*>(getInplaceHolder<T>()); |
834 | 783 | } |
835 | | |
836 | | template <typename T> |
837 | | detail::HolderBase<T, ALLOC>* getHolder(std::false_type) |
838 | 168 | { |
839 | 168 | return static_cast<detail::HolderBase<T, ALLOC>*>(getHeapHolder<T>()); |
840 | 168 | } |
841 | | |
842 | | template <typename T> |
843 | | const detail::HolderBase<T, ALLOC>* getHolder(std::true_type) const |
844 | 32.2k | { |
845 | 32.2k | return static_cast<const detail::HolderBase<T, ALLOC>*>(getInplaceHolder<T>()); |
846 | 32.2k | } |
847 | | |
848 | | template <typename T> |
849 | | const detail::HolderBase<T, ALLOC>* getHolder(std::false_type) const |
850 | 16.4k | { |
851 | 16.4k | return static_cast<const detail::HolderBase<T, ALLOC>*>(getHeapHolder<T>()); |
852 | 16.4k | } |
853 | | |
854 | | detail::IHolder<ALLOC>* getUntypedHolder() |
855 | 107k | { |
856 | 107k | return (m_isInPlace) |
857 | 107k | ? reinterpret_cast<detail::IHolder<ALLOC>*>(&m_untypedHolder.inPlace)74.3k |
858 | 107k | : m_untypedHolder.heap32.9k ; |
859 | 107k | } |
860 | | |
861 | | const detail::IHolder<ALLOC>* getUntypedHolder() const |
862 | 50.7k | { |
863 | 50.7k | return (m_isInPlace) |
864 | 50.7k | ? reinterpret_cast<const detail::IHolder<ALLOC>*>(&m_untypedHolder.inPlace)33.4k |
865 | 50.7k | : m_untypedHolder.heap17.3k ; |
866 | 50.7k | } |
867 | | |
868 | | detail::UntypedHolder<ALLOC> m_untypedHolder; |
869 | | bool m_isInPlace = false; |
870 | | }; |
871 | | |
872 | | } // namespace zserio |
873 | | |
874 | | #endif // ifndef ZSERIO_ANY_HOLDER_H_INC |