GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/zserio/AnyHolder.h Lines: 299 299 100.0 %
Date: 2023-12-13 14:51:09 Branches: 810 2980 27.2 %

Line Branch Exec 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
150266
        static type_id get()
27
        {
28
            static int currentTypeId;
29
30
150266
            return &currentTypeId;
31
        }
32
    };
33
34
    // Interface for object holders
35
    template <typename ALLOC>
36
66256
    class IHolder
37
    {
38
    public:
39

66256
        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

































132512
    class HolderBase : public IHolder<ALLOC>
55
    {
56
    public:
57
        template <typename U>
58
50057
        void set(U&& value)
59
        {
60




50057
            m_typedHolder = std::forward<U>(value);
61
50057
        }
62
63
        template <typename U,
64
                typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
65
4
        void set(NoInitT, U&& value)
66
        {
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
14
        void set(NoInitT, U&& value)
74
        {
75
14
            m_typedHolder = std::forward<U>(value);
76
14
        }
77
78
29
        void setHolder(const inplace_optional_holder<T>& value)
79
        {
80
29
            m_typedHolder = value;
81
29
        }
82
83
16291
        void setHolder(inplace_optional_holder<T>&& value)
84
        {
85
16291
            m_typedHolder = std::move(value);
86
16291
        }
87
88
        template <typename U,
89
                typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
90
6
        void setHolder(NoInitT, const inplace_optional_holder<U>& value)
91
        {
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
3
        void setHolder(NoInitT, inplace_optional_holder<U>&& value)
98
        {
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
19
        void setHolder(NoInitT, const inplace_optional_holder<U>& value)
105
        {
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
19
        void setHolder(NoInitT, inplace_optional_holder<U>&& value)
112
        {
113
19
            m_typedHolder = std::move(value);
114
19
        }
115
116
740
        T& get()
117
        {
118
740
            return m_typedHolder.value();
119
        }
120
121
48656
        const T& get() const
122
        {
123
48656
            return m_typedHolder.value();
124
        }
125
126
75133
        bool isType(detail::TypeIdHolder::type_id typeId) const override
127
        {
128
75133
            return detail::TypeIdHolder::get<T>() == typeId;
129
        }
130
131
    protected:
132
16313
        inplace_optional_holder<T>& getHolder()
133
        {
134
16313
            return m_typedHolder;
135
        }
136
137
54
        const inplace_optional_holder<T>& getHolder() const
138
        {
139
54
            return m_typedHolder;
140
        }
141
142
    private:
143
        inplace_optional_holder<T> m_typedHolder;
144
    };
145
146
    // Holder allocated on heap
147
    template <typename T, typename ALLOC>
148




16631
    class HeapHolder : public HolderBase<T, ALLOC>
149
    {
150
    private:
151
        struct ConstructTag {};
152
153
    public:
154
        using this_type = HeapHolder<T, ALLOC>;
155
156
16631
        explicit HeapHolder(ConstructTag) noexcept {}
157
158
16631
        static this_type* create(const ALLOC& allocator)
159
        {
160
            using AllocType = RebindAlloc<ALLOC, this_type>;
161
            using AllocTraits = std::allocator_traits<AllocType>;
162
163


33220
            AllocType typedAlloc = allocator;
164




16631
            typename AllocTraits::pointer ptr = AllocTraits::allocate(typedAlloc, 1);
165
            // this never throws because HeapHolder constructor never throws
166




16631
            AllocTraits::construct(typedAlloc, std::addressof(*ptr), ConstructTag{});
167
33220
            return ptr;
168
        }
169
170
12
        IHolder<ALLOC>* clone(const ALLOC& allocator) const override
171
        {
172
12
            this_type* holder = create(allocator);
173
12
            holder->setHolder(this->getHolder());
174
12
            return holder;
175
        }
176
177
12
        IHolder<ALLOC>* clone(NoInitT, const ALLOC& allocator) const override
178
        {
179
12
            this_type* holder = create(allocator);
180
12
            holder->setHolder(NoInit, this->getHolder());
181
12
            return holder;
182
        }
183
184
1
        IHolder<ALLOC>* clone(void*) const override
185
        {
186




1
            throw CppRuntimeException("AnyHolder: Unexpected clone call.");
187
        }
188
189
1
        IHolder<ALLOC>* clone(NoInitT, void*) const override
190
        {
191




1
            throw CppRuntimeException("AnyHolder: Unexpected clone call.");
192
        }
193
194
6
        IHolder<ALLOC>* move(const ALLOC& allocator) override
195
        {
196
6
            this_type* holder = create(allocator);
197
6
            holder->setHolder(std::move(this->getHolder()));
198
6
            return holder;
199
        }
200
201
6
        IHolder<ALLOC>* move(NoInitT, const ALLOC& allocator) override
202
        {
203
6
            this_type* holder = create(allocator);
204
6
            holder->setHolder(NoInit, std::move(this->getHolder()));
205
6
            return holder;
206
        }
207
208
1
        IHolder<ALLOC>* move(void*) override
209
        {
210




1
            throw CppRuntimeException("AnyHolder: Unexpected move call.");
211
        }
212
213
1
        IHolder<ALLOC>* move(NoInitT, void*) override
214
        {
215




1
            throw CppRuntimeException("AnyHolder: Unexpected move call.");
216
        }
217
218
16631
        void destroy(const ALLOC& allocator) override
219
        {
220
            using AllocType = RebindAlloc<ALLOC, this_type>;
221
            using AllocTraits = std::allocator_traits<AllocType>;
222
223


33220
            AllocType typedAlloc = allocator;
224




16631
            AllocTraits::destroy(typedAlloc, this);
225



16631
            AllocTraits::deallocate(typedAlloc, this, 1);
226
16631
        }
227
    };
228
229
    // Holder allocated in the in-place storage
230
    template <typename T, typename ALLOC>
231
























49625
    class NonHeapHolder : public HolderBase<T, ALLOC>
232
    {
233
    public:
234
        using this_type = NonHeapHolder<T, ALLOC>;
235
236
33294
        static this_type* create(void* storage)
237
        {
238





































33294
            return new (storage) this_type();
239
        }
240
241
1
        IHolder<ALLOC>* clone(const ALLOC&) const override
242
        {
243
























1
            throw CppRuntimeException("AnyHolder: Unexpected clone call.");
244
        }
245
246
1
        IHolder<ALLOC>* clone(NoInitT, const ALLOC&) const override
247
        {
248
























1
            throw CppRuntimeException("AnyHolder: Unexpected clone call.");
249
        }
250
251
17
        IHolder<ALLOC>* clone(void* storage) const override
252
        {
253









































17
            NonHeapHolder* holder = new (storage) NonHeapHolder();
254
17
            holder->setHolder(this->getHolder());
255
17
            return holder;
256
        }
257
258
13
        IHolder<ALLOC>* clone(NoInitT, void* storage) const override
259
        {
260









































13
            NonHeapHolder* holder = new (storage) NonHeapHolder();
261
13
            holder->setHolder(NoInit, this->getHolder());
262
13
            return holder;
263
        }
264
265
1
        IHolder<ALLOC>* move(const ALLOC&) override
266
        {
267
























1
            throw CppRuntimeException("AnyHolder: Unexpected move call.");
268
        }
269
270
1
        IHolder<ALLOC>* move(NoInitT, const ALLOC&) override
271
        {
272
























1
            throw CppRuntimeException("AnyHolder: Unexpected move call.");
273
        }
274
275
16285
        IHolder<ALLOC>* move(void* storage) override
276
        {
277









































16285
            NonHeapHolder* holder = new (storage) NonHeapHolder();
278
16285
            holder->setHolder(std::move(this->getHolder()));
279
16285
            return holder;
280
        }
281
282
16
        IHolder<ALLOC>* move(NoInitT, void* storage) override
283
        {
284









































16
            NonHeapHolder* holder = new (storage) NonHeapHolder();
285
16
            holder->setHolder(NoInit, std::move(this->getHolder()));
286
16
            return holder;
287
        }
288
289
49625
        void destroy(const ALLOC&) override
290
        {
291
49625
            this->~NonHeapHolder();
292
49625
        }
293
294
    private:
295
49625
        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

375
    AnyHolder() : AnyHolder(ALLOC())
333
    {
334
375
    }
335
336
    /**
337
     * Constructor from given allocator
338
     */
339
944
    explicit AnyHolder(const ALLOC& allocator) :
340
944
            AllocatorHolder<ALLOC>(allocator)
341
    {
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
25140
    explicit AnyHolder(T&& value, const ALLOC& allocator = ALLOC()) :
354
25140
            AllocatorHolder<ALLOC>(allocator)
355
    {
356
25140
        m_untypedHolder.heap = nullptr;
357







































25140
        set(std::forward<T>(value));
358
25140
    }
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
18
    explicit AnyHolder(NoInitT, T&& value, const ALLOC& allocator = ALLOC()) :
368
18
            AllocatorHolder<ALLOC>(allocator)
369
    {
370
18
        m_untypedHolder.heap = nullptr;
371








18
        set(NoInit, std::forward<T>(value));
372
18
    }
373
374
    /**
375
     * Destructor.
376
     */
377
26310
    ~AnyHolder()
378
    {
379
26310
        clearHolder();
380
26310
    }
381
382
    /**
383
     * Copy constructor.
384
     *
385
     * \param other Any holder to copy.
386
     */
387
29
    AnyHolder(const AnyHolder& other) :
388
            AllocatorHolder<ALLOC>(
389
29
                    AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
390
    {
391

29
        copy(other);
392
29
    }
393
394
    /**
395
     * Copy constructor which prevents initialization.
396
     *
397
     * \param other Any holder to copy.
398
     */
399
24
    AnyHolder(NoInitT, const AnyHolder& other) :
400
            AllocatorHolder<ALLOC>(
401
24
                    AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
402
    {
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
8
    AnyHolder(const AnyHolder& other, const ALLOC& allocator) : AllocatorHolder<ALLOC>(allocator)
413
    {
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
8
    AnyHolder(NoInitT, const AnyHolder& other, const ALLOC& allocator) :
424
8
            AllocatorHolder<ALLOC>(allocator)
425
    {
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
24
    AnyHolder& operator=(const AnyHolder& other)
437
    {
438

24
        if (this != &other)
439
        {
440
            // TODO: do not dealloc unless necessary
441
16
            clearHolder();
442
            if (AllocTraits::propagate_on_container_copy_assignment::value)
443
8
                set_allocator(other.get_allocator_ref());
444
16
            copy(other);
445
        }
446
447
24
        return *this;
448
    }
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
37
    AnyHolder& assign(NoInitT, const AnyHolder& other)
458
    {
459

37
        if (this != &other)
460
        {
461
            // TODO: do not dealloc unless necessary
462
29
            clearHolder();
463
            if (AllocTraits::propagate_on_container_copy_assignment::value)
464
8
                set_allocator(other.get_allocator_ref());
465
29
            copy(NoInit, other);
466
        }
467
468
37
        return *this;
469
    }
470
471
    /**
472
     * Move constructor.
473
     *
474
     * \param other Any holder to move from.
475
     */
476
62
    AnyHolder(AnyHolder&& other) noexcept :
477
62
            AllocatorHolder<ALLOC>(std::move(other.get_allocator_ref()))
478
    {
479
62
        move(std::move(other));
480
62
    }
481
482
    /**
483
     * Move constructor which prevents initialization.
484
     *
485
     * \param other Any holder to move from.
486
     */
487
29
    AnyHolder(NoInitT, AnyHolder&& other) noexcept :
488
29
            AllocatorHolder<ALLOC>(std::move(other.get_allocator_ref()))
489
    {
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
24
    AnyHolder(AnyHolder&& other, const ALLOC& allocator) :
500
24
            AllocatorHolder<ALLOC>(allocator)
501
    {
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
24
    AnyHolder(NoInitT, AnyHolder&& other, const ALLOC& allocator) :
512
24
            AllocatorHolder<ALLOC>(allocator)
513
    {
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
48888
    AnyHolder& operator=(AnyHolder&& other)
525
    {
526

48888
        if (this != &other)
527
        {
528
48880
            clearHolder();
529
            if (AllocTraits::propagate_on_container_move_assignment::value)
530
48872
                set_allocator(std::move(other.get_allocator_ref()));
531
48880
            move(std::move(other));
532
        }
533
534
48888
        return *this;
535
    }
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
27
    AnyHolder& assign(NoInitT, AnyHolder&& other)
545
    {
546

27
        if (this != &other)
547
        {
548
19
            clearHolder();
549
            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
        }
553
554
27
        return *this;
555
    }
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
72
    AnyHolder& operator=(T&& value)
567
    {
568
72
        set(std::forward<T>(value));
569
570
72
        return *this;
571
    }
572
573
    /**
574
     * Resets the holder.
575
     */
576
66
    void reset()
577
    {
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
50057
    void set(T&& value)
588
    {
589
50057
        createHolder<typename std::decay<T>::type>()->set(std::forward<T>(value));
590
50057
    }
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
18
    void set(NoInitT, T&& value)
599
    {
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
772
    T& get()
612
    {
613
772
        checkType<T>();
614




















































740
        return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
615
    }
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
48657
    const T& get() const
626
    {
627
48657
        checkType<T>();
628

















48656
        return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
629
    }
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
50549
    bool isType() const
638
    {
639


























































50549
        return hasHolder() && getUntypedHolder()->isType(detail::TypeIdHolder::get<T>());
640
    }
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
24684
    bool hasValue() const
648
    {
649
24684
        return hasHolder();
650
    }
651
652
private:
653
53
    void copy(const AnyHolder& other)
654
    {
655

53
        if (other.m_isInPlace)
656
        {
657
17
            other.getUntypedHolder()->clone(&m_untypedHolder.inPlace);
658
17
            m_isInPlace = true;
659
        }
660

36
        else if (other.m_untypedHolder.heap != nullptr)
661
        {
662
12
            m_untypedHolder.heap = other.getUntypedHolder()->clone(get_allocator_ref());
663
        }
664
        else
665
        {
666
24
            m_untypedHolder.heap = nullptr;
667
        }
668
53
    }
669
670
61
    void copy(NoInitT, const AnyHolder& other)
671
    {
672

61
        if (other.m_isInPlace)
673
        {
674
13
            other.getUntypedHolder()->clone(NoInit, &m_untypedHolder.inPlace);
675
13
            m_isInPlace = true;
676
        }
677

48
        else if (other.m_untypedHolder.heap != nullptr)
678
        {
679
24
            m_untypedHolder.heap = other.getUntypedHolder()->clone(NoInit,
680
12
                    get_allocator_ref());
681
        }
682
        else
683
        {
684
36
            m_untypedHolder.heap = nullptr;
685
        }
686
61
    }
687
688
48966
    void move(AnyHolder&& other)
689
    {
690

48966
        if (other.m_isInPlace)
691
        {
692
16285
            other.getUntypedHolder()->move(&m_untypedHolder.inPlace);
693
16285
            m_isInPlace = true;
694
16285
            other.clearHolder();
695
        }
696

32681
        else if (other.m_untypedHolder.heap != nullptr)
697
        {
698

32645
            if (get_allocator_ref() == other.get_allocator_ref())
699
            {
700
                // take over the other's storage
701
32639
                m_untypedHolder.heap = other.m_untypedHolder.heap;
702
32639
                other.m_untypedHolder.heap = nullptr;
703
            }
704
            else
705
            {
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
            }
710
        }
711
        else
712
        {
713
36
            m_untypedHolder.heap = nullptr;
714
        }
715
48966
    }
716
717
72
    void move(NoInitT, AnyHolder&& other)
718
    {
719

72
        if (other.m_isInPlace)
720
        {
721
16
            other.getUntypedHolder()->move(NoInit, &m_untypedHolder.inPlace);
722
16
            m_isInPlace = true;
723
16
            other.clearHolder();
724
        }
725

56
        else if (other.m_untypedHolder.heap != nullptr)
726
        {
727

17
            if (get_allocator_ref() == other.get_allocator_ref())
728
            {
729
                // take over the other's storage
730
11
                m_untypedHolder.heap = other.m_untypedHolder.heap;
731
11
                other.m_untypedHolder.heap = nullptr;
732
            }
733
            else
734
            {
735
                // cannot steal the storage, allocate our own and move the holder
736
12
                m_untypedHolder.heap = other.getUntypedHolder()->move(NoInit,
737
6
                        get_allocator_ref());
738
6
                other.clearHolder();
739
            }
740
        }
741
        else
742
        {
743
39
            m_untypedHolder.heap = nullptr;
744
        }
745
72
    }
746
747
116045
    void clearHolder()
748
    {
749

116045
        if (hasHolder())
750
        {
751
66254
            getUntypedHolder()->destroy(get_allocator_ref());
752
66254
            m_isInPlace = false;
753
66254
            m_untypedHolder.heap = nullptr;
754
        }
755
116045
    }
756
757
241353
    bool hasHolder() const
758
    {
759


241353
        return (m_isInPlace || m_untypedHolder.heap != nullptr);
760
    }
761
762
    template <typename T>
763
50075
    detail::HolderBase<T, ALLOC>* createHolder()
764
    {
765




























50075
        if (hasHolder())
766
        {
767




























24600
            if (getUntypedHolder()->isType(detail::TypeIdHolder::get<T>()))
768




























188
                return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>());
769
770
24412
            clearHolder();
771
        }
772
773




























49887
        return createHolderImpl<T>(detail::has_non_heap_holder<T, ALLOC>());
774
    }
775
776
    template <typename T>
777
33293
    detail::HolderBase<T, ALLOC>* createHolderImpl(std::true_type)
778
    {
779
        detail::NonHeapHolder<T, ALLOC>* holder =
780
33293
                detail::NonHeapHolder<T, ALLOC>::create(&m_untypedHolder.inPlace);
781
33293
        m_isInPlace = true;
782
33293
        return holder;
783
    }
784
785
    template <typename T>
786
16594
    detail::HolderBase<T, ALLOC>* createHolderImpl(std::false_type)
787
    {
788
16594
        detail::HeapHolder<T, ALLOC>* holder = detail::HeapHolder<T, ALLOC>::create(get_allocator_ref());
789
16594
        m_untypedHolder.heap = holder;
790
16594
        return holder;
791
    }
792
793
    template <typename T>
794
49429
    void checkType() const
795
    {
796





























49429
        if (!isType<T>())
797
33
            throwBadType();
798
49396
    }
799
800
    /** Optimization which increases changes to inline checkType(). */
801
33
    void throwBadType() const
802
    {
803

33
        throw CppRuntimeException("Bad type in AnyHolder");
804
    }
805
806
    template <typename T>
807
168
    detail::HeapHolder<T, ALLOC>* getHeapHolder()
808
    {
809
168
        return static_cast<detail::HeapHolder<T, ALLOC>*>(m_untypedHolder.heap);
810
    }
811
812
    template <typename T>
813
16414
    const detail::HeapHolder<T, ALLOC>* getHeapHolder() const
814
    {
815
16414
        return static_cast<detail::HeapHolder<T, ALLOC>*>(m_untypedHolder.heap);
816
    }
817
818
    template <typename T>
819
760
    detail::NonHeapHolder<T, ALLOC>* getInplaceHolder()
820
    {
821
760
        return reinterpret_cast<detail::NonHeapHolder<T, ALLOC>*>(&m_untypedHolder.inPlace);
822
    }
823
824
    template <typename T>
825
32242
    const detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() const
826
    {
827
32242
        return reinterpret_cast<const detail::NonHeapHolder<T, ALLOC>*>(&m_untypedHolder.inPlace);
828
    }
829
830
    template <typename T>
831
760
    detail::HolderBase<T, ALLOC>* getHolder(std::true_type)
832
    {
833
760
        return static_cast<detail::HolderBase<T, ALLOC>*>(getInplaceHolder<T>());
834
    }
835
836
    template <typename T>
837
168
    detail::HolderBase<T, ALLOC>* getHolder(std::false_type)
838
    {
839
168
        return static_cast<detail::HolderBase<T, ALLOC>*>(getHeapHolder<T>());
840
    }
841
842
    template <typename T>
843
32242
    const detail::HolderBase<T, ALLOC>* getHolder(std::true_type) const
844
    {
845
32242
        return static_cast<const detail::HolderBase<T, ALLOC>*>(getInplaceHolder<T>());
846
    }
847
848
    template <typename T>
849
16414
    const detail::HolderBase<T, ALLOC>* getHolder(std::false_type) const
850
    {
851
16414
        return static_cast<const detail::HolderBase<T, ALLOC>*>(getHeapHolder<T>());
852
    }
853
854
107167
    detail::IHolder<ALLOC>* getUntypedHolder()
855
    {
856
107167
        return (m_isInPlace) ?
857
                reinterpret_cast<detail::IHolder<ALLOC>*>(&m_untypedHolder.inPlace) :
858

107167
                m_untypedHolder.heap;
859
    }
860
861
50587
    const detail::IHolder<ALLOC>* getUntypedHolder() const
862
    {
863
50587
        return (m_isInPlace) ?
864
                reinterpret_cast<const detail::IHolder<ALLOC>*>(&m_untypedHolder.inPlace) :
865

50587
                m_untypedHolder.heap;
866
    }
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