Coverage Report

Created: 2024-04-30 09:35

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/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 &currentTypeId;
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