1 #ifndef ZSERIO_OPTIONAL_HOLDER_H_INC
2 #define ZSERIO_OPTIONAL_HOLDER_H_INC
53 template <
typename T,
typename Derived>
54 class optional_holder_base
64 bool operator==(
const optional_holder_base& other)
const
69 if (getDerived()->hasValue() != other.getDerived()->hasValue())
72 if (getDerived()->hasValue())
73 return get() == other.get();
85 bool operator<(
const optional_holder_base& other)
const
87 if (getDerived()->hasValue() && other.getDerived()->hasValue())
88 return get() < other.get();
90 return !getDerived()->hasValue() && other.getDerived()->hasValue();
98 explicit operator bool() const noexcept
100 return getDerived()->hasValue();
110 const T* operator->()
const
112 return std::addressof(get());
124 return std::addressof(get());
134 const T& value()
const
152 void checkHasValue()
const
154 if (!getDerived()->hasValue())
155 throwNonPresentException();
161 return getDerived()->operator*();
166 return getDerived()->operator*();
169 Derived* getDerived()
171 return static_cast<Derived*
>(
this);
174 const Derived* getDerived()
const
176 return static_cast<const Derived*
>(
this);
180 void throwNonPresentException()
const
182 throw CppRuntimeException(
"Trying to access value of non-present optional field!");
189 template <
typename T,
typename ALLOC>
190 class heap_optional_holder :
public optional_holder_base<T, heap_optional_holder<T, ALLOC>>
193 using allocator_type = ALLOC;
194 using allocator_traits = std::allocator_traits<allocator_type>;
195 using value_type = T;
203 allocator_type get_allocator()
const
205 return m_storage.get_deleter().get_allocator();
213 explicit constexpr heap_optional_holder(
const allocator_type& allocator = allocator_type()) noexcept :
214 m_storage(
nullptr, allocator)
222 constexpr heap_optional_holder(NullOptType,
const allocator_type& allocator = allocator_type()) noexcept :
223 m_storage(
nullptr, allocator)
232 heap_optional_holder(
const T& value,
const allocator_type& allocator = allocator_type()) :
242 heap_optional_holder(T&& value,
const allocator_type& allocator = allocator_type()) :
253 template <
typename U = T,
254 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
255 heap_optional_holder(NoInitT, T&& value,
const allocator_type& allocator = allocator_type()) :
265 template <
typename... U>
266 explicit heap_optional_holder(
const allocator_type& allocator, U&&... parameters) :
273 ~heap_optional_holder() =
default;
280 heap_optional_holder(
const heap_optional_holder& other) :
281 m_storage(copy_initialize(
282 other, allocator_traits::select_on_container_copy_construction(other.get_allocator())))
290 template <
typename U = T,
291 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
292 heap_optional_holder(NoInitT,
const heap_optional_holder& other) :
293 m_storage(copy_initialize(
NoInit, other,
294 allocator_traits::select_on_container_copy_construction(other.get_allocator())))
303 heap_optional_holder(
const heap_optional_holder& other,
const allocator_type& allocator) :
304 m_storage(copy_initialize(other, allocator))
312 heap_optional_holder(heap_optional_holder&& other) noexcept =
default;
319 template <
typename U = T,
320 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
321 heap_optional_holder(NoInitT, heap_optional_holder&& other) :
322 m_storage(move_initialize(
NoInit, std::move(other), other.get_allocator()))
331 heap_optional_holder(heap_optional_holder&& other,
const allocator_type& allocator) :
332 m_storage(move_initialize(std::move(other), allocator))
342 heap_optional_holder& operator=(
const heap_optional_holder& other)
347 m_storage = copy_initialize(other,
348 select_allocator(other.get_allocator(),
349 typename allocator_traits::propagate_on_container_copy_assignment()));
361 template <
typename U = T,
362 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
363 heap_optional_holder& assign(NoInitT,
const heap_optional_holder& other)
368 m_storage = copy_initialize(
NoInit, other,
369 select_allocator(other.get_allocator(),
370 typename allocator_traits::propagate_on_container_copy_assignment()));
382 heap_optional_holder& operator=(heap_optional_holder&& other)
387 m_storage = move_initialize(std::move(other),
388 select_allocator(other.get_allocator(),
389 typename allocator_traits::propagate_on_container_move_assignment()));
401 template <
typename U = T,
402 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
403 heap_optional_holder& assign(NoInitT, heap_optional_holder&& other)
408 m_storage = move_initialize(
NoInit, std::move(other),
409 select_allocator(other.get_allocator(),
410 typename allocator_traits::propagate_on_container_move_assignment()));
422 heap_optional_holder& operator=(
const T& value)
436 heap_optional_holder& operator=(T&& value)
438 set(std::move(value));
446 void reset() noexcept
456 bool hasValue() const noexcept
458 return static_cast<bool>(m_storage);
468 const T& operator*()
const
470 this->checkHasValue();
471 return *m_storage.get();
483 this->checkHasValue();
484 return *m_storage.get();
488 allocator_type select_allocator(allocator_type other_allocator, std::true_type)
490 return other_allocator;
493 allocator_type select_allocator(allocator_type, std::false_type)
495 return get_allocator();
498 template <
typename U = T>
502 m_storage = zserio::allocate_unique<T, allocator_type>(get_allocator(), std::forward<U>(value));
505 static storage_type copy_initialize(
const heap_optional_holder& other,
const allocator_type& allocator)
507 if (other.hasValue())
508 return zserio::allocate_unique<T, allocator_type>(allocator, *other);
510 return storage_type(
nullptr, allocator);
513 static storage_type copy_initialize(
514 NoInitT,
const heap_optional_holder& other,
const allocator_type& allocator)
516 if (other.hasValue())
517 return zserio::allocate_unique<T, allocator_type>(allocator,
NoInit, *other);
519 return storage_type(
nullptr, allocator);
522 static storage_type move_initialize(heap_optional_holder&& other,
const allocator_type& allocator)
524 if (other.hasValue())
526 if (allocator == other.get_allocator())
527 return std::move(other.m_storage);
529 return zserio::allocate_unique<T, allocator_type>(allocator, std::move(*other));
533 return storage_type(
nullptr, allocator);
537 static storage_type move_initialize(NoInitT, heap_optional_holder&& other,
const allocator_type& allocator)
539 if (other.hasValue())
541 if (allocator == other.get_allocator())
542 return std::move(other.m_storage);
544 return zserio::allocate_unique<T, allocator_type>(allocator,
NoInit, std::move(*other));
548 return storage_type(
nullptr, allocator);
552 storage_type m_storage;
558 template <
typename T>
559 class in_place_storage
565 in_place_storage() =
default;
570 ~in_place_storage() =
default;
574 in_place_storage(
const in_place_storage&) =
delete;
575 in_place_storage& operator=(
const in_place_storage&) =
delete;
579 in_place_storage(in_place_storage&& other) =
delete;
588 in_place_storage& operator=(in_place_storage&& other)
590 new (&m_inPlace) T(std::move(*other.getObject()));
591 other.getObject()->~T();
603 in_place_storage& assign(NoInitT, in_place_storage&& other)
605 new (&m_inPlace) T(
NoInit, std::move(*other.getObject()));
606 other.getObject()->~T();
628 return reinterpret_cast<T*
>(&m_inPlace);
636 const T* getObject()
const
638 return reinterpret_cast<const T*
>(&m_inPlace);
646 static constexpr
size_t SIZEOF_T =
sizeof(T);
647 static constexpr
size_t ALIGNOF_T =
alignof(T);
648 using AlignedStorage =
typename std::aligned_storage<SIZEOF_T, ALIGNOF_T>::type;
649 AlignedStorage m_inPlace;
655 template <
typename T>
656 class inplace_optional_holder :
public optional_holder_base<T, inplace_optional_holder<T>>
659 using value_type = T;
664 constexpr inplace_optional_holder() noexcept = default;
669 constexpr inplace_optional_holder(NullOptType) noexcept
677 inplace_optional_holder(
const T& value)
679 new (m_storage.getStorage()) T(value);
688 inplace_optional_holder(T&& value)
690 new (m_storage.getStorage()) T(std::move(value));
700 template <
typename U = T,
701 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
702 inplace_optional_holder(NoInitT, T&& value)
704 new (m_storage.getStorage()) T(
NoInit, std::move(value));
713 inplace_optional_holder(
const inplace_optional_holder& other)
715 if (other.hasValue())
717 new (m_storage.getStorage()) T(*other.m_storage.getObject());
727 template <
typename U = T,
728 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
729 inplace_optional_holder(NoInitT,
const inplace_optional_holder& other)
731 if (other.hasValue())
733 new (m_storage.getStorage()) T(
NoInit, *other.m_storage.getObject());
743 inplace_optional_holder(inplace_optional_holder&& other) noexcept(
744 std::is_nothrow_move_constructible<in_place_storage<T>>::value)
746 if (other.hasValue())
748 m_storage = std::move(other.m_storage);
749 other.m_hasValue =
false;
759 template <
typename U = T,
760 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
761 inplace_optional_holder(NoInitT, inplace_optional_holder&& other) noexcept(
762 std::is_nothrow_move_constructible<in_place_storage<T>>::value)
764 if (other.hasValue())
766 m_storage.assign(
NoInit, std::move(other.m_storage));
767 other.m_hasValue =
false;
777 template <
typename... U>
778 explicit inplace_optional_holder(InPlaceT, U&&... parameters)
780 new (m_storage.getStorage()) T(std::forward<U>(parameters)...);
787 ~inplace_optional_holder()
799 inplace_optional_holder& operator=(
const inplace_optional_holder& other)
804 if (other.hasValue())
806 new (m_storage.getStorage()) T(*other.m_storage.getObject());
821 template <
typename U = T,
822 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
823 inplace_optional_holder& assign(NoInitT,
const inplace_optional_holder& other)
828 if (other.hasValue())
830 new (m_storage.getStorage()) T(
NoInit, *other.m_storage.getObject());
845 inplace_optional_holder& operator=(inplace_optional_holder&& other)
850 if (other.hasValue())
852 m_storage = std::move(other.m_storage);
853 other.m_hasValue =
false;
868 template <
typename U = T,
869 typename std::enable_if<std::is_constructible<U, NoInitT, U>::value,
int>::type = 0>
870 inplace_optional_holder& assign(NoInitT, inplace_optional_holder&& other)
875 if (other.hasValue())
877 m_storage.assign(
NoInit, std::move(other.m_storage));
878 other.m_hasValue =
false;
893 inplace_optional_holder& operator=(
const T& value)
907 inplace_optional_holder& operator=(T&& value)
909 set(std::move(value));
917 void reset() noexcept
921 m_storage.getObject()->~T();
931 bool hasValue() const noexcept
943 const T& operator*()
const
945 this->checkHasValue();
946 return *m_storage.getObject();
958 this->checkHasValue();
959 return *m_storage.getObject();
963 template <
typename U = T>
967 new (m_storage.getStorage()) T(std::forward<U>(value));
971 in_place_storage<T> m_storage;
972 bool m_hasValue =
false;
982 template <
typename T>
988 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)