GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/zserio/OptionalHolder.h Lines: 260 260 100.0 %
Date: 2023-12-13 14:51:09 Branches: 419 1647 25.4 %

Line Branch Exec Source
1
#ifndef ZSERIO_OPTIONAL_HOLDER_H_INC
2
#define ZSERIO_OPTIONAL_HOLDER_H_INC
3
4
#include <cstddef>
5
#include <type_traits>
6
7
#include "zserio/CppRuntimeException.h"
8
#include "zserio/NoInit.h"
9
#include "zserio/Types.h"
10
#include "zserio/UniquePtr.h"
11
12
namespace zserio
13
{
14
15
/**
16
 * Helper type for specification of an unset optional holder.
17
 */
18
struct NullOptType
19
{
20
    /**
21
     * Explicit constructor from int.
22
     *
23
     * \see https://en.cppreference.com/w/cpp/utility/optional/nullopt_t
24
     */
25
    constexpr explicit NullOptType(int) {}
26
};
27
28
/**
29
 * Constant used to convenient specification of an unset optional holder.
30
 */
31
constexpr NullOptType NullOpt{int()};
32
33
/**
34
 * Helper type for specification of in-place construction.
35
 */
36
struct InPlaceT
37
{
38
    constexpr explicit InPlaceT() = default;
39
};
40
41
/**
42
  * Constant used as a marker for in-place construction.
43
  */
44
constexpr InPlaceT InPlace{};
45
46
namespace detail
47
{
48
49
/**
50
 * Base class for optional holders. Provides common interface for various types of optional holders.
51
 */
52
template <typename T, typename Derived>
53
66980
class optional_holder_base
54
{
55
public:
56
    /**
57
     * Operator equality.
58
     *
59
     * \param other Other holder to compare.
60
     *
61
     * \return True when the other holder has same value as this. False otherwise.
62
     */
63
31
    bool operator==(const optional_holder_base& other) const
64
    {
65


31
        if (this == &other)
66
5
            return true;
67
68


26
        if (getDerived()->hasValue() != other.getDerived()->hasValue())
69
5
            return false;
70
71


21
        if (getDerived()->hasValue())
72
14
            return get() == other.get();
73
74
7
        return true;
75
    }
76
77
    /**
78
     * Operator less than.
79
     *
80
     * \param other Other holder to compare.
81
     *
82
     * \return True when the other holder has less value than this. False otherwise.
83
     */
84
10
    bool operator<(const optional_holder_base& other) const
85
    {
86






10
        if (getDerived()->hasValue() && other.getDerived()->hasValue())
87
4
            return get() < other.get();
88
89




6
        return !getDerived()->hasValue() && other.getDerived()->hasValue();
90
    }
91
92
    /**
93
     * Bool operator.
94
     *
95
     * Evaluates to true when this holder has assigned any value.
96
     */
97
501
    explicit operator bool() const noexcept
98
    {
99
501
        return getDerived()->hasValue();
100
    }
101
102
    /**
103
     * Dereference operator ->.
104
     *
105
     * \return Const reference to the assigned value.
106
     *
107
     * \throw CppRuntimeException when the holder is unset.
108
     */
109
4
    const T* operator->() const
110
    {
111
4
        return std::addressof(get());
112
    }
113
114
    /**
115
     * Dereference operator ->.
116
     *
117
     * \return Reference to the assigned value.
118
     *
119
     * \throw CppRuntimeException when the holder is unset.
120
     */
121
426
    T* operator->()
122
    {
123
426
        return std::addressof(get());
124
    }
125
126
    /**
127
     * Gets held value.
128
     *
129
     * \return Const reference to the assigned value.
130
     *
131
     * \throw CppRuntimeException when the holder is unset.
132
     */
133
48713
    const T& value() const
134
    {
135
48713
        return get();
136
    }
137
138
    /**
139
     * Gets held value.
140
     *
141
     * \return Reference to the assigned value.
142
     *
143
     * \throw CppRuntimeException when the holder is unset.
144
     */
145
859
    T& value()
146
    {
147
859
        return get();
148
    }
149
150
protected:
151
50156
    void checkHasValue() const
152
    {
153





























50156
        if (!getDerived()->hasValue())
154
14
            throwNonPresentException();
155
50142
    }
156
157
private:
158
1285
    T& get()
159
    {
160
1285
        return getDerived()->operator*();
161
    }
162
163
48753
    const T& get() const
164
    {
165
48753
        return getDerived()->operator*();
166
    }
167
168
1285
    Derived* getDerived()
169
    {
170
1285
        return static_cast<Derived*>(this);
171
    }
172
173
99509
    const Derived* getDerived() const
174
    {
175
99509
        return static_cast<const Derived*>(this);
176
    }
177
178
    /** Optimization which increases chances to inline checkHasValue(). */
179
14
    void throwNonPresentException() const
180
    {
181





























14
        throw CppRuntimeException("Trying to access value of non-present optional field!");
182
    }
183
};
184
185
/**
186
 * Optional holder implementation that stores the object on heap.
187
 */
188
template <typename T, typename ALLOC>
189
class heap_optional_holder : public optional_holder_base<T, heap_optional_holder<T, ALLOC>>
190
{
191
public:
192
    using allocator_type = ALLOC;
193
    using allocator_traits = std::allocator_traits<allocator_type>;
194
    using value_type = T;
195
    using storage_type = zserio::unique_ptr<T, allocator_type>;
196
197
    /**
198
     * Getter for the allocator.
199
     *
200
     * \return Allocator that is used for the dynamic memory allocat
201
     */
202
98
    allocator_type get_allocator() const
203
    {
204
98
        return m_storage.get_deleter().get_allocator();
205
    }
206
207
    /**
208
     * Empty constructor which creates an unset holder.
209
     *
210
     * \param allocator Allocator to be used to perform dynamic memory allocations.
211
     */
212
45
    explicit constexpr heap_optional_holder(const allocator_type& allocator = allocator_type()) noexcept :
213
45
            m_storage(nullptr, allocator)
214
45
    {}
215
216
    /**
217
     * Constructor from zserio::NullOpt constant to create an unset holder.
218
     *
219
     * \param allocator Allocator to be used to perform dynamic memory allocations.
220
     */
221
2
    constexpr heap_optional_holder(NullOptType,
222
2
            const allocator_type& allocator = allocator_type()) noexcept : m_storage(nullptr, allocator)
223
2
    {}
224
225
    /**
226
     * Constructor from a given value.
227
     *
228
     * \param value Value to store in the holder.
229
     * \param allocator Allocator to be used to perform dynamic memory allocations.
230
     */
231
2
    heap_optional_holder(const T& value, const allocator_type& allocator = allocator_type()) :
232
2
            m_storage(zserio::allocate_unique<T, allocator_type>(allocator, value))
233
2
    {}
234
235
    /**
236
     * Constructor from a given value passed by rvalue reference.
237
     *
238
     * \param value Value to store in the holder.
239
     * \param allocator Allocator to be used to perform dynamic memory allocations.
240
     */
241
27
    heap_optional_holder(T&& value, const allocator_type& allocator = allocator_type()) :
242
27
            m_storage(zserio::allocate_unique<T, allocator_type>(allocator, std::move(value)))
243
27
    {}
244
245
    // called from allocatorPropagatingCopy
246
    /**
247
     * Constructor from a given value passed by rvalue reference which prevents initialization.
248
     *
249
     * \param value Value to store in the holder.
250
     * \param allocator Allocator to be used to perform dynamic memory allocations.
251
     */
252
    template <typename U = T,
253
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
254
2
    heap_optional_holder(NoInitT, T&& value, const allocator_type& allocator = allocator_type()) :
255
2
            m_storage(zserio::allocate_unique<T, allocator_type>(allocator, NoInit, std::move(value)))
256
2
    {}
257
258
    /**
259
     * Constructor that initializes the value inplace by forwarding the arguments to the object's constructor.
260
     *
261
     * \param allocator Allocator to be used for dynamic memory allocations.
262
     * \param u Parameters for object's constructor.
263
     */
264
    template <typename ...U>
265
2
    explicit heap_optional_holder(const allocator_type& allocator, U&& ...u) :
266
2
            m_storage(zserio::allocate_unique<T, allocator_type>(allocator, std::forward<U>(u)...))
267
2
    {}
268
269
    /**
270
     * Destructor.
271
     */
272
98
    ~heap_optional_holder() = default;
273
274
    /**
275
     * Copy constructor.
276
     *
277
     * \param other Other holder to copy.
278
     */
279
6
    heap_optional_holder(const heap_optional_holder& other) :
280
            m_storage(copy_initialize(other, allocator_traits::select_on_container_copy_construction(
281




6
                    other.get_allocator())))
282
6
    {}
283
284
    /**
285
     * Copy constructor which prevents initialization.
286
     *
287
     * \param other Other holder to copy.
288
     */
289
    template <typename U = T,
290
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
291
2
    heap_optional_holder(NoInitT, const heap_optional_holder& other) :
292
            m_storage(copy_initialize(NoInit, other,
293
                    allocator_traits::select_on_container_copy_construction(
294

2
                            other.get_allocator())))
295
2
    {}
296
297
    /**
298
     * Allocator-extended copy constructor.
299
     *
300
     * \param other Other holder to copy.
301
     * \param allocator Allocator to be used for dynamic memory allocations.
302
     */
303
2
    heap_optional_holder(const heap_optional_holder& other, const allocator_type& allocator) :
304
2
            m_storage(copy_initialize(other, allocator))
305
2
    {}
306
307
    /**
308
     * Move constructor.
309
     *
310
     * \param other Other holder to move.
311
     */
312
1
    heap_optional_holder(heap_optional_holder&& other) noexcept = default;
313
314
    /**
315
     * Move constructor which prevents initialization.
316
     *
317
     * \param other Other holder to move.
318
     */
319
    template <typename U = T,
320
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
321
4
    heap_optional_holder(NoInitT, heap_optional_holder&& other) :
322
4
            m_storage(move_initialize(NoInit, std::move(other), other.get_allocator()))
323
    {
324
4
    }
325
326
    /**
327
     * Allocator-extended move constructor.
328
     *
329
     * \param other Other holder to move.
330
     * \param allocator Allocator to be used for dynamic memory allocations.
331
     */
332
3
    heap_optional_holder(heap_optional_holder&& other, const allocator_type& allocator) :
333
3
            m_storage(move_initialize(std::move(other), allocator))
334
    {
335
3
    }
336
337
    /**
338
     * Assignment operator.
339
     *
340
     * \param other Other holder to copy-assign.
341
     *
342
     * \return Reference to the current holder.
343
     */
344
5
    heap_optional_holder& operator=(const heap_optional_holder& other)
345
    {
346


5
        if (this == &other)
347
2
            return *this;
348
349





3
        m_storage = copy_initialize(other, select_allocator(other.get_allocator(),
350
                typename allocator_traits::propagate_on_container_copy_assignment()));
351
352
3
        return *this;
353
    }
354
355
    /**
356
     * Assignment operator which prevents initialization.
357
     *
358
     * \param other Other holder to copy-assign.
359
     *
360
     * \return Reference to the current holder.
361
     */
362
    template <typename U = T,
363
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
364
3
    heap_optional_holder& assign(NoInitT, const heap_optional_holder& other)
365
    {
366
3
        if (this == &other)
367
1
            return *this;
368
369

2
        m_storage = copy_initialize(NoInit, other, select_allocator(other.get_allocator(),
370
                typename allocator_traits::propagate_on_container_copy_assignment()));
371
372
2
        return *this;
373
    }
374
375
    /**
376
     * Move assignment operator.
377
     *
378
     * \param other Other holder to move-assign.
379
     *
380
     * \return Reference to the current holder.
381
     */
382
4
    heap_optional_holder& operator=(heap_optional_holder&& other)
383
    {
384

4
        if (this == &other)
385
2
            return *this;
386
387



2
        m_storage = move_initialize(std::move(other), select_allocator(other.get_allocator(),
388
                typename allocator_traits::propagate_on_container_move_assignment()));
389
390
2
        return *this;
391
    }
392
393
    /**
394
     * Move assignment operator which prevents initialization.
395
     *
396
     * \param other Other holder to move-assign.
397
     *
398
     * \return Reference to the current holder.
399
     */
400
    template <typename U = T,
401
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
402
5
    heap_optional_holder& assign(NoInitT, heap_optional_holder&& other)
403
    {
404

5
        if (this == &other)
405
2
            return *this;
406
407


3
        m_storage = move_initialize(NoInit, std::move(other),
408
                select_allocator(other.get_allocator(),
409
                        typename allocator_traits::propagate_on_container_move_assignment()));
410
411
3
        return *this;
412
    }
413
414
    /**
415
     * Assignment operator from value.
416
     *
417
     * \param value Value to assign.
418
     *
419
     * \return Reference to the current holder.
420
     */
421
8
    heap_optional_holder& operator=(const T& value)
422
    {
423
8
        set(value);
424
425
8
        return *this;
426
    }
427
428
    /**
429
     * Assignment operator from rvalue reference to value.
430
     *
431
     * \param value Value to move-assign.
432
     *
433
     * \return Reference to the current holder.
434
     */
435
7
    heap_optional_holder& operator=(T&& value)
436
    {
437
7
        set(std::move(value));
438
439
7
        return *this;
440
    }
441
442
    /**
443
     * Resets the current holder (switch to the unset state).
444
     */
445
17
    void reset() noexcept
446
    {
447
17
        m_storage.reset();
448
17
    }
449
450
    /**
451
     * Gets whether the holder has any value.
452
     *
453
     * \return True when this holder has assigned any value. False otherwise.
454
     */
455
208
    bool hasValue() const noexcept
456
    {
457
208
        return static_cast<bool>(m_storage);
458
    }
459
460
    /**
461
     * Dereference operator *.
462
     *
463
     * \return Const reference to the assigned value.
464
     *
465
     * \throw CppRuntimeException when the holder is unset.
466
     */
467
34
    const T& operator*() const
468
    {
469
34
        this->checkHasValue();
470
34
        return *m_storage.get();
471
    }
472
473
    /**
474
     * Dereference operator *.
475
     *
476
     * \return Reference to the assigned value.
477
     *
478
     * \throw CppRuntimeException when the holder is unset.
479
     */
480
76
    T& operator*()
481
    {
482
76
        this->checkHasValue();
483
68
        return *m_storage.get();
484
    }
485
486
private:
487
7
    allocator_type select_allocator(allocator_type other_allocator, std::true_type)
488
    {
489
7
        return other_allocator;
490
    }
491
492
3
    allocator_type select_allocator(allocator_type, std::false_type)
493
    {
494
3
        return get_allocator(); // current allocator
495
    }
496
497
    template <typename U = T>
498
15
    void set(U&& value)
499
    {
500
15
        reset();
501




15
        m_storage = zserio::allocate_unique<T, allocator_type>(get_allocator(), std::forward<U>(value));
502
15
    }
503
504
11
    static storage_type copy_initialize(const heap_optional_holder& other, const allocator_type& allocator)
505
    {
506


11
        if (other.hasValue())
507
9
            return zserio::allocate_unique<T, allocator_type>(allocator, *other);
508
        else
509
2
            return storage_type(nullptr, allocator);
510
    }
511
512
4
    static storage_type copy_initialize(NoInitT, const heap_optional_holder& other,
513
            const allocator_type& allocator)
514
    {
515
4
        if (other.hasValue())
516
2
            return zserio::allocate_unique<T, allocator_type>(allocator, NoInit, *other);
517
        else
518
2
            return storage_type(nullptr, allocator);
519
    }
520
521
5
    static storage_type move_initialize(heap_optional_holder&& other, const allocator_type& allocator)
522
    {
523


5
        if (other.hasValue())
524
        {
525


4
            if (allocator == other.get_allocator())
526
2
                return std::move(other.m_storage);
527
528
2
            return zserio::allocate_unique<T, allocator_type>(allocator, std::move(*other));
529
        }
530
        else
531
        {
532
1
            return storage_type(nullptr, allocator);
533
        }
534
    }
535
536
7
    static storage_type move_initialize(NoInitT, heap_optional_holder&& other,
537
            const allocator_type& allocator)
538
    {
539

7
        if (other.hasValue())
540
        {
541

4
            if (allocator == other.get_allocator())
542
3
                return std::move(other.m_storage);
543
544
            return zserio::allocate_unique<T, allocator_type>(allocator, NoInit,
545
1
                    std::move(*other));
546
        }
547
        else
548
        {
549
3
            return storage_type(nullptr, allocator);
550
        }
551
    }
552
553
    storage_type m_storage;
554
};
555
556
/**
557
 * In place storage for optional holder.
558
 */
559
template <typename T>
560
class in_place_storage
561
{
562
public:
563
    /**
564
     * Constructor.
565
     */
566
66883
    in_place_storage() = default;
567
568
    /**
569
     * Destructor.
570
     */
571
    ~in_place_storage() = default;
572
573
    /** Copying is disabled. */
574
    /** \{ */
575
    in_place_storage(const in_place_storage&) = delete;
576
    in_place_storage& operator=(const in_place_storage&) = delete;
577
    /** \} */
578
579
    /** Move constructor is disabled. */
580
    in_place_storage(in_place_storage&& other) = delete;
581
582
    /**
583
     * Move assignment operator.
584
     *
585
     * \param other Other storage to move.
586
     *
587
     * \return Reference to the current storage.
588
     */
589
16313
    in_place_storage& operator=(in_place_storage&& other)
590
    {
591









































16313
        new (&m_inPlace) T(std::move(*other.getObject()));
592
16313
        other.getObject()->~T(); // ensure that destructor of object in original storage is called
593
594
16313
        return *this;
595
    }
596
597
    /**
598
     * Move assignment operator which prevents initialization.
599
     *
600
     * \param other Other storage to move.
601
     *
602
     * \return Reference to the current storage.
603
     */
604
10
    in_place_storage& assign(NoInitT, in_place_storage&& other)
605
    {
606

10
        new (&m_inPlace) T(NoInit, std::move(*other.getObject()));
607
10
        other.getObject()->~T(); // ensure that destructor of object in original storage is called
608
609
10
        return *this;
610
    }
611
612
    /**
613
     * Gets pointer to the underlying in-place memory.
614
     *
615
     * \return Pointer to the storage.
616
     */
617
50425
    void* getStorage()
618
    {
619
50425
        return &m_inPlace;
620
    }
621
622
    /**
623
     * Gets pointer to the stored object.
624
     *
625
     * \return Pointer to the object.
626
     */
627
84370
    T* getObject()
628
    {
629
84370
        return reinterpret_cast<T*>(&m_inPlace);
630
    }
631
632
    /**
633
     * Gets pointer to the stored object.
634
     *
635
     * \return Const pointer to the object.
636
     */
637
48852
    const T* getObject() const
638
    {
639
48852
        return reinterpret_cast<const T*>(&m_inPlace);
640
    }
641
642
private:
643
    // Caution!
644
    // We use static constants as a WORKAROUND for a GCC 8/9 bug observed when cross-compiling with -m32 flag.
645
    // The compilers somehow spoils the aligned storage when alignof(T) is used directly as the template
646
    // argument which leads to "*** stack smashing detected ***" error (as observed in some language tests).
647
    static constexpr size_t SIZEOF_T = sizeof(T);
648
    static constexpr size_t ALIGNOF_T = alignof(T);
649
    using AlignedStorage = typename std::aligned_storage<SIZEOF_T, ALIGNOF_T>::type;
650
    AlignedStorage m_inPlace;
651
};
652
653
/**
654
 * Optional holder implementation for Zserio which allows usage of in place storage.
655
 */
656
template <typename T>
657
class inplace_optional_holder : public optional_holder_base<T, inplace_optional_holder<T>>
658
{
659
public:
660
    using value_type = T;
661
662
    /**
663
     * Empty constructor which creates an unset holder.
664
     */
665
66421
    constexpr inplace_optional_holder() noexcept = default;
666
667
    /**
668
     * Constructor from zserio::NullOpt constant to create an unset holder.
669
     */
670
293
    constexpr inplace_optional_holder(NullOptType) noexcept
671
293
    {}
672
673
    /**
674
     * Constructor from a given value.
675
     *
676
     * \param value Value to store in the holder.
677
     */
678
42
    inplace_optional_holder(const T& value)
679
42
    {
680


42
        new (m_storage.getStorage()) T(value);
681
42
        m_hasValue = true;
682
42
    }
683
684
    /**
685
     * Constructor from a given value passed by rvalue reference.
686
     *
687
     * \param value Value to store in the holder.
688
     */
689
36
    inplace_optional_holder(T&& value)
690
36
    {
691




36
        new (m_storage.getStorage()) T(std::move(value));
692
36
        m_hasValue = true;
693
36
    }
694
695
    // called from allocatorPropagatingCopy
696
    /**
697
     * Constructor from a given value passed by rvalue reference which prevents initialization.
698
     *
699
     * \param value Value to store in the holder.
700
     */
701
    template <typename U = T,
702
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
703
6
    inplace_optional_holder(NoInitT, T&& value)
704
6
    {
705

6
        new (m_storage.getStorage()) T(NoInit, std::move(value));
706
6
        m_hasValue = true;
707
6
    }
708
709
    /**
710
     * Copy constructor.
711
     *
712
     * \param other Other holder to copy.
713
     */
714
72
    inplace_optional_holder(const inplace_optional_holder& other)
715
72
    {
716

72
        if (other.hasValue())
717
        {
718



44
            new (m_storage.getStorage()) T(*other.m_storage.getObject());
719
44
            m_hasValue = true;
720
        }
721
72
    }
722
723
    /**
724
     * Copy constructor which prevents initialization.
725
     *
726
     * \param other Other holder to copy.
727
     */
728
    template <typename U = T,
729
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
730
2
    inplace_optional_holder(NoInitT, const inplace_optional_holder& other)
731
2
    {
732
2
        if (other.hasValue())
733
        {
734

1
            new (m_storage.getStorage()) T(NoInit, *other.m_storage.getObject());
735
1
            m_hasValue = true;
736
        }
737
2
    }
738
739
    /**
740
     * Move constructor.
741
     *
742
     * \param other Other holder to move.
743
     */
744
4
    inplace_optional_holder(inplace_optional_holder&& other)
745
            noexcept(std::is_nothrow_move_constructible<in_place_storage<T>>::value)
746
4
    {
747

4
        if (other.hasValue())
748
        {
749
2
            m_storage = std::move(other.m_storage);
750
2
            other.m_hasValue = false;
751
2
            m_hasValue = true;
752
        }
753
4
    }
754
755
    /**
756
     * Move constructor which prevents initialization.
757
     *
758
     * \param other Other holder to move.
759
     */
760
    template <typename U = T,
761
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
762
4
    inplace_optional_holder(NoInitT, inplace_optional_holder&& other)
763
            noexcept(std::is_nothrow_move_constructible<in_place_storage<T>>::value)
764
4
    {
765
4
        if (other.hasValue())
766
        {
767
2
            m_storage.assign(NoInit, std::move(other.m_storage));
768
2
            other.m_hasValue = false;
769
2
            m_hasValue = true;
770
        }
771
4
    }
772
773
    /**
774
     * Constructor that initializes the value inplace by forwarding the arguments to the object's constructor.
775
     *
776
     * \param u Parameters for object's constructor.
777
     */
778
    template <typename ...U>
779
3
    explicit inplace_optional_holder(InPlaceT, U&& ...u)
780
3
    {
781

3
        new (m_storage.getStorage()) T(std::forward<U>(u)...);
782
3
        m_hasValue = true;
783
3
    }
784
785
    /**
786
     * Destructor.
787
     */
788
66883
    ~inplace_optional_holder()
789
    {
790
66883
        reset();
791
66883
    }
792
793
    /**
794
     * Assignment operator.
795
     *
796
     * \param other Other holder to copy-assign.
797
     *
798
     * \return Reference to the current holder.
799
     */
800
61
    inplace_optional_holder& operator=(const inplace_optional_holder& other)
801
    {
802




























61
        if (this != &other)
803
        {
804
60
            reset();
805




























60
            if (other.hasValue())
806
            {
807













































59
                new (m_storage.getStorage()) T(*other.m_storage.getObject());
808
59
                m_hasValue = true;
809
            }
810
        }
811
812
61
        return *this;
813
    }
814
815
    /**
816
     * Assignment operator which prevents initialization.
817
     *
818
     * \param other Other holder to copy-assign.
819
     *
820
     * \return Reference to the current holder.
821
     */
822
    template <typename U = T,
823
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
824
9
    inplace_optional_holder& assign(NoInitT, const inplace_optional_holder& other)
825
    {
826
9
        if (this != &other)
827
        {
828
8
            reset();
829
8
            if (other.hasValue())
830
            {
831

7
                new (m_storage.getStorage()) T(NoInit, *other.m_storage.getObject());
832
7
                m_hasValue = true;
833
            }
834
        }
835
836
9
        return *this;
837
    }
838
839
    /**
840
     * Move assignment operator.
841
     *
842
     * \param other Other holder to move-assign.
843
     *
844
     * \return Reference to the current holder.
845
     */
846
16329
    inplace_optional_holder& operator=(inplace_optional_holder&& other)
847
    {
848




























16329
        if (this != &other)
849
        {
850
16328
            reset();
851




























16328
            if (other.hasValue())
852
            {
853
16311
                m_storage = std::move(other.m_storage);
854
16311
                other.m_hasValue = false;
855
16311
                m_hasValue = true;
856
            }
857
        }
858
859
16329
        return *this;
860
    }
861
862
    /**
863
     * Move assignment operator which prevents initialization.
864
     *
865
     * \param other Other holder to move-assign.
866
     *
867
     * \return Reference to the current holder.
868
     */
869
    template <typename U = T,
870
            typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
871
10
    inplace_optional_holder& assign(NoInitT, inplace_optional_holder&& other)
872
    {
873
10
        if (this != &other)
874
        {
875
9
            reset();
876
9
            if (other.hasValue())
877
            {
878
8
                m_storage.assign(NoInit, std::move(other.m_storage));
879
8
                other.m_hasValue = false;
880
8
                m_hasValue = true;
881
            }
882
        }
883
884
10
        return *this;
885
    }
886
887
    /**
888
     * Assignment operator from value.
889
     *
890
     * \param value Value to assign.
891
     *
892
     * \return Reference to the current holder.
893
     */
894
33378
    inplace_optional_holder& operator=(const T& value)
895
    {
896
33378
        set(value);
897
898
33378
        return *this;
899
    }
900
901
    /**
902
     * Assignment operator from rvalue reference to value.
903
     *
904
     * \param value Value to move-assign.
905
     *
906
     * \return Reference to the current holder.
907
     */
908
16849
    inplace_optional_holder& operator=(T&& value)
909
    {
910
16849
        set(std::move(value));
911
912
16849
        return *this;
913
    }
914
915
    /**
916
     * Resets the current holder (switch to the unset state).
917
     */
918
133563
    void reset() noexcept
919
    {
920




























133563
        if (hasValue())
921
        {
922
50425
            m_storage.getObject()->~T();
923
50425
            m_hasValue = false;
924
        }
925
133563
    }
926
927
    /**
928
     * Gets whether the holder has any value.
929
     *
930
     * \return True when this holder has assigned any value. False otherwise.
931
     */
932
201187
    bool hasValue() const noexcept
933
    {
934
201187
        return m_hasValue;
935
    }
936
937
    /**
938
     * Dereference operator *.
939
     *
940
     * \return Const reference to the assigned value.
941
     *
942
     * \throw CppRuntimeException when the holder is unset.
943
     */
944
48741
    const T& operator*() const
945
    {
946
48741
        this->checkHasValue();
947
48741
        return *m_storage.getObject();
948
    }
949
950
    /**
951
     * Dereference operator *.
952
     *
953
     * \return Reference to the assigned value.
954
     *
955
     * \throw CppRuntimeException when the holder is unset.
956
     */
957
1305
    T& operator*()
958
    {
959
1305
        this->checkHasValue();
960
1299
        return *m_storage.getObject();
961
    }
962
963
private:
964
    template <typename U = T>
965
50227
    void set(U&& value)
966
    {
967
50227
        reset();
968



















































50227
        new (m_storage.getStorage()) T(std::forward<U>(value));
969
50227
        m_hasValue = true;
970
50227
    }
971
972
    in_place_storage<T> m_storage;
973
    bool m_hasValue = false;
974
};
975
976
} // namespace detail
977
978
// Be aware that if Inplace/HeapOptionalHolder is defined by typename, C++ compiler will have problem with
979
// template function overload, see HashCodeUtil.h (overloads for objects and for Inplace/HeapOptionalHolder).
980
/**
981
 * Optional holder which uses in place storage.
982
 */
983
template <typename T>
984
using InplaceOptionalHolder = detail::inplace_optional_holder<T>;
985
986
/**
987
 * Optional holder which uses heap storage.
988
 */
989
template <typename T, typename ALLOC = std::allocator<T>>
990
using HeapOptionalHolder = detail::heap_optional_holder<T, ALLOC>;
991
992
} // namespace zserio
993
994
#endif // ifndef ZSERIO_OPTIONAL_HOLDER_H_INC