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