Coverage Report

Created: 2023-12-13 14:58

src/zserio/AnyHolder.h
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 &currentTypeId;
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