1 #ifndef ZSERIO_OPTIONAL_HOLDER_H_INC
2 #define ZSERIO_OPTIONAL_HOLDER_H_INC
52 template <
typename T,
typename Derived>
53 class optional_holder_base
63 bool operator==(
const optional_holder_base& other)
const
70 if (getDerived()->hasValue() != other.getDerived()->hasValue())
75 if (getDerived()->hasValue())
77 return get() == other.get();
90 bool operator<(
const optional_holder_base& other)
const
92 if (getDerived()->hasValue() && other.getDerived()->hasValue())
94 return get() < other.get();
97 return !getDerived()->hasValue() && other.getDerived()->hasValue();
105 explicit operator bool() const noexcept
107 return getDerived()->hasValue();
117 const T* operator->()
const
119 return std::addressof(get());
131 return std::addressof(get());
141 const T& value()
const
159 void checkHasValue()
const
161 if (!getDerived()->hasValue())
163 throwNonPresentException();
170 return getDerived()->operator*();
175 return getDerived()->operator*();
178 Derived* getDerived()
180 return static_cast<Derived*
>(
this);
183 const Derived* getDerived()
const
185 return static_cast<const Derived*
>(
this);
189 void throwNonPresentException()
const
191 throw CppRuntimeException(
"Trying to access value of non-present optional field!");
198 template <
typename T,
typename ALLOC>
199 class heap_optional_holder :
public optional_holder_base<T, heap_optional_holder<T, ALLOC>>
202 using allocator_type = ALLOC;
203 using allocator_traits = std::allocator_traits<allocator_type>;
204 using value_type = T;
212 allocator_type get_allocator()
const
214 return m_storage.get_deleter().get_allocator();
222 explicit constexpr heap_optional_holder(
const allocator_type& allocator = allocator_type()) noexcept :
223 m_storage(
nullptr, allocator)
231 constexpr heap_optional_holder(NullOptType,
const allocator_type& allocator = allocator_type()) noexcept :
232 m_storage(
nullptr, allocator)
241 heap_optional_holder(
const T& val,
const allocator_type& allocator = allocator_type()) :
251 heap_optional_holder(T&& val,
const allocator_type& allocator = allocator_type()) :
262 template <
typename U = T,
263 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
264 heap_optional_holder(NoInitT, T&& val,
const allocator_type& allocator = allocator_type()) :
274 template <
typename... U>
275 explicit heap_optional_holder(
const allocator_type& allocator, U&&... parameters) :
282 ~heap_optional_holder() =
default;
289 heap_optional_holder(
const heap_optional_holder& other) :
290 m_storage(copy_initialize(
291 other, allocator_traits::select_on_container_copy_construction(other.get_allocator())))
299 template <
typename U = T,
300 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
301 heap_optional_holder(NoInitT,
const heap_optional_holder& other) :
302 m_storage(copy_initialize(
NoInit, other,
303 allocator_traits::select_on_container_copy_construction(other.get_allocator())))
312 heap_optional_holder(
const heap_optional_holder& other,
const allocator_type& allocator) :
313 m_storage(copy_initialize(other, allocator))
321 heap_optional_holder(heap_optional_holder&& other) noexcept =
default;
328 template <
typename U = T,
329 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
330 heap_optional_holder(NoInitT, heap_optional_holder&& other) :
331 m_storage(move_initialize(
NoInit, std::move(other), other.get_allocator()))
340 heap_optional_holder(heap_optional_holder&& other,
const allocator_type& allocator) :
341 m_storage(move_initialize(std::move(other), allocator))
351 heap_optional_holder& operator=(
const heap_optional_holder& other)
358 m_storage = copy_initialize(other,
359 select_allocator(other.get_allocator(),
360 typename allocator_traits::propagate_on_container_copy_assignment()));
372 template <
typename U = T,
373 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
374 heap_optional_holder& assign(NoInitT,
const heap_optional_holder& other)
381 m_storage = copy_initialize(
NoInit, other,
382 select_allocator(other.get_allocator(),
383 typename allocator_traits::propagate_on_container_copy_assignment()));
395 heap_optional_holder& operator=(heap_optional_holder&& other)
402 m_storage = move_initialize(std::move(other),
403 select_allocator(other.get_allocator(),
404 typename allocator_traits::propagate_on_container_move_assignment()));
416 template <
typename U = T,
417 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
418 heap_optional_holder& assign(NoInitT, heap_optional_holder&& other)
425 m_storage = move_initialize(
NoInit, std::move(other),
426 select_allocator(other.get_allocator(),
427 typename allocator_traits::propagate_on_container_move_assignment()));
439 heap_optional_holder& operator=(
const T& val)
453 heap_optional_holder& operator=(T&& val)
463 void reset() noexcept
473 bool hasValue() const noexcept
475 return static_cast<bool>(m_storage);
485 const T& operator*()
const
487 this->checkHasValue();
488 return *m_storage.get();
500 this->checkHasValue();
501 return *m_storage.get();
505 allocator_type select_allocator(allocator_type other_allocator, std::true_type)
507 return other_allocator;
510 allocator_type select_allocator(allocator_type, std::false_type)
512 return get_allocator();
515 template <
typename U = T>
519 m_storage = zserio::allocate_unique<T, allocator_type>(get_allocator(), std::forward<U>(val));
522 static storage_type copy_initialize(
const heap_optional_holder& other,
const allocator_type& allocator)
524 if (other.hasValue())
526 return zserio::allocate_unique<T, allocator_type>(allocator, *other);
530 return storage_type(
nullptr, allocator);
534 static storage_type copy_initialize(
535 NoInitT,
const heap_optional_holder& other,
const allocator_type& allocator)
537 if (other.hasValue())
539 return zserio::allocate_unique<T, allocator_type>(allocator,
NoInit, *other);
543 return storage_type(
nullptr, allocator);
547 static storage_type move_initialize(heap_optional_holder&& other,
const allocator_type& allocator)
549 if (other.hasValue())
551 if (allocator == other.get_allocator())
553 return std::move(other.m_storage);
556 return zserio::allocate_unique<T, allocator_type>(allocator, std::move(*other));
560 return storage_type(
nullptr, allocator);
564 static storage_type move_initialize(NoInitT, heap_optional_holder&& other,
const allocator_type& allocator)
566 if (other.hasValue())
568 if (allocator == other.get_allocator())
570 return std::move(other.m_storage);
573 return zserio::allocate_unique<T, allocator_type>(allocator,
NoInit, std::move(*other));
577 return storage_type(
nullptr, allocator);
581 storage_type m_storage;
587 template <
typename T>
588 class in_place_storage
594 in_place_storage() =
default;
599 ~in_place_storage() =
default;
603 in_place_storage(
const in_place_storage&) =
delete;
604 in_place_storage& operator=(
const in_place_storage&) =
delete;
608 in_place_storage(in_place_storage&& other) =
delete;
617 in_place_storage& operator=(in_place_storage&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
619 new (&m_inPlace) T(std::move(*other.getObject()));
620 other.getObject()->~T();
632 in_place_storage& assign(NoInitT, in_place_storage&& other) noexcept(
633 std::is_nothrow_move_constructible<T>::value)
635 new (&m_inPlace) T(
NoInit, std::move(*other.getObject()));
636 other.getObject()->~T();
658 return reinterpret_cast<T*
>(&m_inPlace);
666 const T* getObject()
const
668 return reinterpret_cast<const T*
>(&m_inPlace);
676 static constexpr
size_t SIZEOF_T =
sizeof(T);
677 static constexpr
size_t ALIGNOF_T =
alignof(T);
678 using AlignedStorage =
typename std::aligned_storage<SIZEOF_T, ALIGNOF_T>::type;
679 AlignedStorage m_inPlace;
685 template <
typename T>
686 class inplace_optional_holder :
public optional_holder_base<T, inplace_optional_holder<T>>
689 using value_type = T;
694 constexpr inplace_optional_holder() noexcept = default;
699 constexpr inplace_optional_holder(NullOptType) noexcept
707 inplace_optional_holder(
const T& val)
709 new (m_storage.getStorage()) T(val);
718 inplace_optional_holder(T&& val) noexcept(std::is_nothrow_constructible<T>::value)
720 new (m_storage.getStorage()) T(std::move(val));
730 template <
typename U = T,
731 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
732 inplace_optional_holder(NoInitT, T&& val) noexcept(std::is_nothrow_move_constructible<T>::value)
734 new (m_storage.getStorage()) T(
NoInit, std::move(val));
743 inplace_optional_holder(
const inplace_optional_holder& other)
745 if (other.hasValue())
747 new (m_storage.getStorage()) T(*other.m_storage.getObject());
757 template <
typename U = T,
758 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
759 inplace_optional_holder(NoInitT,
const inplace_optional_holder& other)
761 if (other.hasValue())
763 new (m_storage.getStorage()) T(
NoInit, *other.m_storage.getObject());
773 inplace_optional_holder(inplace_optional_holder&& other) noexcept(
774 std::is_nothrow_move_assignable<in_place_storage<T>>::value)
776 if (other.hasValue())
778 m_storage = std::move(other.m_storage);
779 other.m_hasValue =
false;
789 template <
typename U = T,
790 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
791 inplace_optional_holder(NoInitT, inplace_optional_holder&& other) noexcept(
792 std::is_nothrow_move_assignable<in_place_storage<T>>::value)
794 if (other.hasValue())
796 m_storage.assign(
NoInit, std::move(other.m_storage));
797 other.m_hasValue =
false;
807 template <
typename... U>
808 explicit inplace_optional_holder(InPlaceT, U&&... parameters)
810 new (m_storage.getStorage()) T(std::forward<U>(parameters)...);
817 ~inplace_optional_holder()
829 inplace_optional_holder& operator=(
const inplace_optional_holder& other)
834 if (other.hasValue())
836 new (m_storage.getStorage()) T(*other.m_storage.getObject());
851 template <
typename U = T,
852 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
853 inplace_optional_holder& assign(NoInitT,
const inplace_optional_holder& other)
858 if (other.hasValue())
860 new (m_storage.getStorage()) T(
NoInit, *other.m_storage.getObject());
875 inplace_optional_holder& operator=(inplace_optional_holder&& other) noexcept(
876 std::is_nothrow_move_assignable<in_place_storage<T>>::value)
881 if (other.hasValue())
883 m_storage = std::move(other.m_storage);
884 other.m_hasValue =
false;
899 template <
typename U = T,
900 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
901 inplace_optional_holder& assign(NoInitT, inplace_optional_holder&& other) noexcept(
902 std::is_nothrow_move_assignable<in_place_storage<T>>::value)
907 if (other.hasValue())
909 m_storage.assign(
NoInit, std::move(other.m_storage));
910 other.m_hasValue =
false;
925 inplace_optional_holder& operator=(
const T& val)
939 inplace_optional_holder& operator=(T&& val)
949 void reset() noexcept
953 m_storage.getObject()->~T();
963 bool hasValue() const noexcept
975 const T& operator*()
const
977 this->checkHasValue();
978 return *m_storage.getObject();
990 this->checkHasValue();
991 return *m_storage.getObject();
995 template <
typename U = T>
999 new (m_storage.getStorage()) T(std::forward<U>(val));
1003 in_place_storage<T> m_storage;
1004 bool m_hasValue =
false;
1014 template <
typename T>
1020 template <
typename T,
typename ALLOC = std::allocator<T>>
std::set< T, COMPARE, PropagatingPolymorphicAllocator< T > > set
zserio::unique_ptr< T, RebindAlloc< ALLOC, T > > allocate_unique(const ALLOC &allocator, Args &&... args)
constexpr bool operator<(BasicStringView< CharT, Traits > lhs, BasicStringView< CharT, Traits > rhs) noexcept
constexpr NullOptType NullOpt
constexpr bool operator==(BasicStringView< CharT, Traits > lhs, BasicStringView< CharT, Traits > rhs) noexcept
detail::heap_optional_holder< T, ALLOC > HeapOptionalHolder
std::unique_ptr< T, detail::UniquePtrDeleter< ALLOC > > unique_ptr
constexpr InPlaceT InPlace
detail::inplace_optional_holder< T > InplaceOptionalHolder
constexpr InPlaceT()=default
constexpr NullOptType(int)