Zserio C++ runtime library  1.0.0
Built for Zserio 2.13.0
OptionalHolder.h
Go to the documentation of this file.
1 #ifndef ZSERIO_OPTIONAL_HOLDER_H_INC
2 #define ZSERIO_OPTIONAL_HOLDER_H_INC
3 
4 #include <cstddef>
5 #include <type_traits>
6 
8 #include "zserio/NoInit.h"
9 #include "zserio/Types.h"
10 #include "zserio/UniquePtr.h"
11 
12 namespace zserio
13 {
14 
19 {
25  constexpr explicit NullOptType(int) {}
26 };
27 
31 constexpr NullOptType NullOpt{int()};
32 
36 struct InPlaceT
37 {
38  constexpr explicit InPlaceT() = default;
39 };
40 
44 constexpr InPlaceT InPlace{};
45 
46 namespace detail
47 {
48 
52 template <typename T, typename Derived>
53 class optional_holder_base
54 {
55 public:
63  bool operator==(const optional_holder_base& other) const
64  {
65  if (this == &other)
66  return true;
67 
68  if (getDerived()->hasValue() != other.getDerived()->hasValue())
69  return false;
70 
71  if (getDerived()->hasValue())
72  return get() == other.get();
73 
74  return true;
75  }
76 
84  bool operator<(const optional_holder_base& other) const
85  {
86  if (getDerived()->hasValue() && other.getDerived()->hasValue())
87  return get() < other.get();
88 
89  return !getDerived()->hasValue() && other.getDerived()->hasValue();
90  }
91 
97  explicit operator bool() const noexcept
98  {
99  return getDerived()->hasValue();
100  }
101 
109  const T* operator->() const
110  {
111  return std::addressof(get());
112  }
113 
121  T* operator->()
122  {
123  return std::addressof(get());
124  }
125 
133  const T& value() const
134  {
135  return get();
136  }
137 
145  T& value()
146  {
147  return get();
148  }
149 
150 protected:
151  void checkHasValue() const
152  {
153  if (!getDerived()->hasValue())
154  throwNonPresentException();
155  }
156 
157 private:
158  T& get()
159  {
160  return getDerived()->operator*();
161  }
162 
163  const T& get() const
164  {
165  return getDerived()->operator*();
166  }
167 
168  Derived* getDerived()
169  {
170  return static_cast<Derived*>(this);
171  }
172 
173  const Derived* getDerived() const
174  {
175  return static_cast<const Derived*>(this);
176  }
177 
179  void throwNonPresentException() const
180  {
181  throw CppRuntimeException("Trying to access value of non-present optional field!");
182  }
183 };
184 
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 
202  allocator_type get_allocator() const
203  {
204  return m_storage.get_deleter().get_allocator();
205  }
206 
212  explicit constexpr heap_optional_holder(const allocator_type& allocator = allocator_type()) noexcept :
213  m_storage(nullptr, allocator)
214  {}
215 
221  constexpr heap_optional_holder(NullOptType,
222  const allocator_type& allocator = allocator_type()) noexcept : m_storage(nullptr, allocator)
223  {}
224 
231  heap_optional_holder(const T& value, const allocator_type& allocator = allocator_type()) :
232  m_storage(zserio::allocate_unique<T, allocator_type>(allocator, value))
233  {}
234 
241  heap_optional_holder(T&& value, const allocator_type& allocator = allocator_type()) :
242  m_storage(zserio::allocate_unique<T, allocator_type>(allocator, std::move(value)))
243  {}
244 
245  // called from allocatorPropagatingCopy
252  template <typename U = T,
253  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
254  heap_optional_holder(NoInitT, T&& value, const allocator_type& allocator = allocator_type()) :
255  m_storage(zserio::allocate_unique<T, allocator_type>(allocator, NoInit, std::move(value)))
256  {}
257 
264  template <typename ...U>
265  explicit heap_optional_holder(const allocator_type& allocator, U&& ...u) :
266  m_storage(zserio::allocate_unique<T, allocator_type>(allocator, std::forward<U>(u)...))
267  {}
268 
272  ~heap_optional_holder() = default;
273 
279  heap_optional_holder(const heap_optional_holder& other) :
280  m_storage(copy_initialize(other, allocator_traits::select_on_container_copy_construction(
281  other.get_allocator())))
282  {}
283 
289  template <typename U = T,
290  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
291  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  other.get_allocator())))
295  {}
296 
303  heap_optional_holder(const heap_optional_holder& other, const allocator_type& allocator) :
304  m_storage(copy_initialize(other, allocator))
305  {}
306 
312  heap_optional_holder(heap_optional_holder&& other) noexcept = default;
313 
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()))
323  {
324  }
325 
332  heap_optional_holder(heap_optional_holder&& other, const allocator_type& allocator) :
333  m_storage(move_initialize(std::move(other), allocator))
334  {
335  }
336 
344  heap_optional_holder& operator=(const heap_optional_holder& other)
345  {
346  if (this == &other)
347  return *this;
348 
349  m_storage = copy_initialize(other, select_allocator(other.get_allocator(),
350  typename allocator_traits::propagate_on_container_copy_assignment()));
351 
352  return *this;
353  }
354 
362  template <typename U = T,
363  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
364  heap_optional_holder& assign(NoInitT, const heap_optional_holder& other)
365  {
366  if (this == &other)
367  return *this;
368 
369  m_storage = copy_initialize(NoInit, other, select_allocator(other.get_allocator(),
370  typename allocator_traits::propagate_on_container_copy_assignment()));
371 
372  return *this;
373  }
374 
382  heap_optional_holder& operator=(heap_optional_holder&& other)
383  {
384  if (this == &other)
385  return *this;
386 
387  m_storage = move_initialize(std::move(other), select_allocator(other.get_allocator(),
388  typename allocator_traits::propagate_on_container_move_assignment()));
389 
390  return *this;
391  }
392 
400  template <typename U = T,
401  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
402  heap_optional_holder& assign(NoInitT, heap_optional_holder&& other)
403  {
404  if (this == &other)
405  return *this;
406 
407  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  return *this;
412  }
413 
421  heap_optional_holder& operator=(const T& value)
422  {
423  set(value);
424 
425  return *this;
426  }
427 
435  heap_optional_holder& operator=(T&& value)
436  {
437  set(std::move(value));
438 
439  return *this;
440  }
441 
445  void reset() noexcept
446  {
447  m_storage.reset();
448  }
449 
455  bool hasValue() const noexcept
456  {
457  return static_cast<bool>(m_storage);
458  }
459 
467  const T& operator*() const
468  {
469  this->checkHasValue();
470  return *m_storage.get();
471  }
472 
480  T& operator*()
481  {
482  this->checkHasValue();
483  return *m_storage.get();
484  }
485 
486 private:
487  allocator_type select_allocator(allocator_type other_allocator, std::true_type)
488  {
489  return other_allocator;
490  }
491 
492  allocator_type select_allocator(allocator_type, std::false_type)
493  {
494  return get_allocator(); // current allocator
495  }
496 
497  template <typename U = T>
498  void set(U&& value)
499  {
500  reset();
501  m_storage = zserio::allocate_unique<T, allocator_type>(get_allocator(), std::forward<U>(value));
502  }
503 
504  static storage_type copy_initialize(const heap_optional_holder& other, const allocator_type& allocator)
505  {
506  if (other.hasValue())
507  return zserio::allocate_unique<T, allocator_type>(allocator, *other);
508  else
509  return storage_type(nullptr, allocator);
510  }
511 
512  static storage_type copy_initialize(NoInitT, const heap_optional_holder& other,
513  const allocator_type& allocator)
514  {
515  if (other.hasValue())
516  return zserio::allocate_unique<T, allocator_type>(allocator, NoInit, *other);
517  else
518  return storage_type(nullptr, allocator);
519  }
520 
521  static storage_type move_initialize(heap_optional_holder&& other, const allocator_type& allocator)
522  {
523  if (other.hasValue())
524  {
525  if (allocator == other.get_allocator())
526  return std::move(other.m_storage);
527 
528  return zserio::allocate_unique<T, allocator_type>(allocator, std::move(*other));
529  }
530  else
531  {
532  return storage_type(nullptr, allocator);
533  }
534  }
535 
536  static storage_type move_initialize(NoInitT, heap_optional_holder&& other,
537  const allocator_type& allocator)
538  {
539  if (other.hasValue())
540  {
541  if (allocator == other.get_allocator())
542  return std::move(other.m_storage);
543 
544  return zserio::allocate_unique<T, allocator_type>(allocator, NoInit,
545  std::move(*other));
546  }
547  else
548  {
549  return storage_type(nullptr, allocator);
550  }
551  }
552 
553  storage_type m_storage;
554 };
555 
559 template <typename T>
560 class in_place_storage
561 {
562 public:
566  in_place_storage() = default;
567 
571  ~in_place_storage() = default;
572 
575  in_place_storage(const in_place_storage&) = delete;
576  in_place_storage& operator=(const in_place_storage&) = delete;
580  in_place_storage(in_place_storage&& other) = delete;
581 
589  in_place_storage& operator=(in_place_storage&& other)
590  {
591  new (&m_inPlace) T(std::move(*other.getObject()));
592  other.getObject()->~T(); // ensure that destructor of object in original storage is called
593 
594  return *this;
595  }
596 
604  in_place_storage& assign(NoInitT, in_place_storage&& other)
605  {
606  new (&m_inPlace) T(NoInit, std::move(*other.getObject()));
607  other.getObject()->~T(); // ensure that destructor of object in original storage is called
608 
609  return *this;
610  }
611 
617  void* getStorage()
618  {
619  return &m_inPlace;
620  }
621 
627  T* getObject()
628  {
629  return reinterpret_cast<T*>(&m_inPlace);
630  }
631 
637  const T* getObject() const
638  {
639  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 
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 
665  constexpr inplace_optional_holder() noexcept = default;
666 
670  constexpr inplace_optional_holder(NullOptType) noexcept
671  {}
672 
678  inplace_optional_holder(const T& value)
679  {
680  new (m_storage.getStorage()) T(value);
681  m_hasValue = true;
682  }
683 
689  inplace_optional_holder(T&& value)
690  {
691  new (m_storage.getStorage()) T(std::move(value));
692  m_hasValue = true;
693  }
694 
695  // called from allocatorPropagatingCopy
701  template <typename U = T,
702  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
703  inplace_optional_holder(NoInitT, T&& value)
704  {
705  new (m_storage.getStorage()) T(NoInit, std::move(value));
706  m_hasValue = true;
707  }
708 
714  inplace_optional_holder(const inplace_optional_holder& other)
715  {
716  if (other.hasValue())
717  {
718  new (m_storage.getStorage()) T(*other.m_storage.getObject());
719  m_hasValue = true;
720  }
721  }
722 
728  template <typename U = T,
729  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
730  inplace_optional_holder(NoInitT, const inplace_optional_holder& other)
731  {
732  if (other.hasValue())
733  {
734  new (m_storage.getStorage()) T(NoInit, *other.m_storage.getObject());
735  m_hasValue = true;
736  }
737  }
738 
744  inplace_optional_holder(inplace_optional_holder&& other)
745  noexcept(std::is_nothrow_move_constructible<in_place_storage<T>>::value)
746  {
747  if (other.hasValue())
748  {
749  m_storage = std::move(other.m_storage);
750  other.m_hasValue = false;
751  m_hasValue = true;
752  }
753  }
754 
760  template <typename U = T,
761  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
762  inplace_optional_holder(NoInitT, inplace_optional_holder&& other)
763  noexcept(std::is_nothrow_move_constructible<in_place_storage<T>>::value)
764  {
765  if (other.hasValue())
766  {
767  m_storage.assign(NoInit, std::move(other.m_storage));
768  other.m_hasValue = false;
769  m_hasValue = true;
770  }
771  }
772 
778  template <typename ...U>
779  explicit inplace_optional_holder(InPlaceT, U&& ...u)
780  {
781  new (m_storage.getStorage()) T(std::forward<U>(u)...);
782  m_hasValue = true;
783  }
784 
788  ~inplace_optional_holder()
789  {
790  reset();
791  }
792 
800  inplace_optional_holder& operator=(const inplace_optional_holder& other)
801  {
802  if (this != &other)
803  {
804  reset();
805  if (other.hasValue())
806  {
807  new (m_storage.getStorage()) T(*other.m_storage.getObject());
808  m_hasValue = true;
809  }
810  }
811 
812  return *this;
813  }
814 
822  template <typename U = T,
823  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
824  inplace_optional_holder& assign(NoInitT, const inplace_optional_holder& other)
825  {
826  if (this != &other)
827  {
828  reset();
829  if (other.hasValue())
830  {
831  new (m_storage.getStorage()) T(NoInit, *other.m_storage.getObject());
832  m_hasValue = true;
833  }
834  }
835 
836  return *this;
837  }
838 
846  inplace_optional_holder& operator=(inplace_optional_holder&& other)
847  {
848  if (this != &other)
849  {
850  reset();
851  if (other.hasValue())
852  {
853  m_storage = std::move(other.m_storage);
854  other.m_hasValue = false;
855  m_hasValue = true;
856  }
857  }
858 
859  return *this;
860  }
861 
869  template <typename U = T,
870  typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0>
871  inplace_optional_holder& assign(NoInitT, inplace_optional_holder&& other)
872  {
873  if (this != &other)
874  {
875  reset();
876  if (other.hasValue())
877  {
878  m_storage.assign(NoInit, std::move(other.m_storage));
879  other.m_hasValue = false;
880  m_hasValue = true;
881  }
882  }
883 
884  return *this;
885  }
886 
894  inplace_optional_holder& operator=(const T& value)
895  {
896  set(value);
897 
898  return *this;
899  }
900 
908  inplace_optional_holder& operator=(T&& value)
909  {
910  set(std::move(value));
911 
912  return *this;
913  }
914 
918  void reset() noexcept
919  {
920  if (hasValue())
921  {
922  m_storage.getObject()->~T();
923  m_hasValue = false;
924  }
925  }
926 
932  bool hasValue() const noexcept
933  {
934  return m_hasValue;
935  }
936 
944  const T& operator*() const
945  {
946  this->checkHasValue();
947  return *m_storage.getObject();
948  }
949 
957  T& operator*()
958  {
959  this->checkHasValue();
960  return *m_storage.getObject();
961  }
962 
963 private:
964  template <typename U = T>
965  void set(U&& value)
966  {
967  reset();
968  new (m_storage.getStorage()) T(std::forward<U>(value));
969  m_hasValue = true;
970  }
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).
983 template <typename T>
984 using InplaceOptionalHolder = detail::inplace_optional_holder<T>;
985 
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
std::unique_ptr< T, detail::UniquePtrDeleter< ALLOC >> unique_ptr
Definition: UniquePtr.h:89
detail::heap_optional_holder< T, ALLOC > HeapOptionalHolder
constexpr NoInitT NoInit
Definition: NoInit.h:18
constexpr bool operator<(BasicStringView< CharT, Traits > lhs, BasicStringView< CharT, Traits > rhs) noexcept
Definition: StringView.h:843
constexpr bool operator==(BasicStringView< CharT, Traits > lhs, BasicStringView< CharT, Traits > rhs) noexcept
Definition: StringView.h:817
detail::inplace_optional_holder< T > InplaceOptionalHolder
constexpr NullOptType(int)
constexpr NullOptType NullOpt
constexpr InPlaceT InPlace