Zserio C++17 runtime library  0.5.0
Built for Zserio 2.17.0
Optional.h
Go to the documentation of this file.
1 #ifndef ZSERIO_OPTIONAL_H_INC
2 #define ZSERIO_OPTIONAL_H_INC
3 
4 #include <optional>
5 
8 #include "zserio/HashCodeUtil.h"
9 #include "zserio/Traits.h"
10 #include "zserio/View.h"
11 
12 namespace zserio
13 {
14 
15 template <typename ALLOC, typename T>
16 class BasicOptional;
17 
18 namespace detail
19 {
20 
21 template <typename T, bool = (sizeof(T) > 3 * sizeof(void*))>
22 struct is_optional_big : std::false_type
23 {};
24 
25 template <typename T>
26 struct is_optional_big<T, true> : std::true_type
27 {};
28 
29 template <typename T, typename = void>
30 struct is_recursive : std::false_type
31 {};
32 
33 template <typename T>
34 struct is_recursive<T, std::void_t<typename T::IS_RECURSIVE>> : std::true_type
35 {};
36 
37 template <typename T, bool = is_recursive<T>::value>
38 struct is_optional_heap_allocated_impl : is_optional_big<T>::type
39 {};
40 
41 template <typename T>
42 struct is_optional_heap_allocated_impl<T, true> : std::true_type
43 {};
44 
45 template <typename T>
46 struct is_optional_heap_allocated : is_optional_heap_allocated_impl<T>
47 {};
48 
49 template <typename T>
50 struct is_optional_heap_allocated<View<T>> : std::false_type
51 {};
52 
53 template <typename T>
54 constexpr bool is_optional_heap_allocated_v = is_optional_heap_allocated<T>::value;
55 
56 template <typename T, bool heap = is_optional_heap_allocated_v<T>>
57 struct optional_element
58 {
59  using type = T;
60 };
61 
62 template <typename T>
63 struct optional_element<T, true>
64 {
65  using type = T*;
66 };
67 
68 template <typename T>
69 struct is_optional : std::false_type
70 {};
71 
72 template <typename A, typename T>
73 struct is_optional<BasicOptional<A, T>> : std::true_type
74 {};
75 
76 template <typename T>
77 constexpr bool is_optional_v = is_optional<T>::value;
78 
79 } // namespace detail
80 
85 {
86 public:
88 };
89 
104 template <typename ALLOC, typename T>
105 class BasicOptional : public AllocatorHolder<ALLOC>
106 {
107  using AllocTraits = std::allocator_traits<ALLOC>;
109 
110 public:
112  using allocator_type = ALLOC;
113  using OptionalType = std::optional<typename detail::optional_element<T>::type>;
114 
118  constexpr BasicOptional() noexcept :
119  BasicOptional(ALLOC())
120  {}
121 
125  constexpr BasicOptional(std::nullopt_t, const ALLOC& allocator = {}) noexcept :
126  BasicOptional(allocator)
127  {}
128 
135  constexpr BasicOptional(const T& value, const ALLOC& allocator = {}) :
136  BasicOptional(std::in_place, allocator, value)
137  {}
138 
145  constexpr BasicOptional(T&& value, const ALLOC& allocator = {}) :
146  BasicOptional(std::in_place, allocator, std::move(value))
147  {}
148 
154  explicit constexpr BasicOptional(const ALLOC& allocator) :
155  AllocatorHolder<ALLOC>(allocator)
156  {}
157 
166  template <typename... ARGS, typename TT = T,
167  std::enable_if_t<!detail::is_optional_heap_allocated_v<TT>>* = nullptr,
168  std::enable_if_t<!is_first_allocator<ARGS...>::value>* = nullptr>
169  explicit constexpr BasicOptional(std::in_place_t, ARGS&&... args) :
170  m_data(std::in_place, std::forward<ARGS>(args)...)
171  {}
172 
182  template <typename... ARGS, typename TT = T,
183  std::enable_if_t<!detail::is_optional_heap_allocated_v<TT>>* = nullptr>
184  constexpr BasicOptional(std::in_place_t, const ALLOC& allocator, ARGS&&... args) :
185  AllocatorHolder<ALLOC>(allocator),
186  m_data(std::in_place, std::forward<ARGS>(args)...)
187  {}
188 
198  template <typename... ARGS, typename TT = T,
199  std::enable_if_t<detail::is_optional_heap_allocated_v<TT>>* = nullptr>
200  constexpr BasicOptional(std::in_place_t, const ALLOC& allocator, ARGS&&... args) :
201  AllocatorHolder<ALLOC>(allocator),
202  m_data(std::in_place, allocateValue(std::forward<ARGS>(args)...))
203  {}
204 
213  template <typename... ARGS, typename TT = T,
214  std::enable_if_t<detail::is_optional_heap_allocated_v<TT>>* = nullptr,
215  std::enable_if_t<!is_first_allocator<ARGS...>::value>* = nullptr>
216  explicit constexpr BasicOptional(std::in_place_t, ARGS&&... args) :
217  m_data(std::in_place, allocateValue(std::forward<ARGS>(args)...))
218  {}
219 
224  {
225  clear();
226  }
227 
233  constexpr BasicOptional(const BasicOptional& other) :
234  AllocatorHolder<ALLOC>(
235  AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
236  {
237  copy(other);
238  }
239 
245  template <typename U>
246  constexpr BasicOptional(const BasicOptional<ALLOC, U>& other) :
247  AllocatorHolder<ALLOC>(
248  AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
249  {
250  copy(other);
251  }
252 
261  constexpr BasicOptional(const BasicOptional& other, const ALLOC& allocator) :
262  AllocatorHolder<ALLOC>(allocator)
263  {
264  copy(other);
265  }
266 
275  template <typename A, typename U>
276  BasicOptional(const BasicOptional<A, U>& other, const ALLOC& allocator) :
277  AllocatorHolder<ALLOC>(allocator)
278  {
279  copy(other);
280  }
281 
291  constexpr BasicOptional& operator=(const BasicOptional& other)
292  {
293  if (this != &other)
294  {
295  clear();
296  if constexpr (AllocTraits::propagate_on_container_copy_assignment::value)
297  {
299  }
300  copy(other);
301  }
302 
303  return *this;
304  }
305 
315  template <typename A, typename U>
317  {
318  clear();
319  if constexpr (AllocTraits::propagate_on_container_copy_assignment::value)
320  {
322  }
323  copy(other);
324 
325  return *this;
326  }
327 
333  BasicOptional& operator=(std::nullopt_t)
334  {
335  reset();
336  return *this;
337  }
338 
346  template <typename U = T,
347  std::enable_if_t<!detail::is_optional_v<std::decay_t<U>> &&
348  !(std::is_same_v<T, std::decay_t<U>> && std::is_scalar_v<U>)>* = nullptr>
349  BasicOptional& operator=(U&& value)
350  {
351  emplace(std::forward<U>(value));
352  return *this;
353  }
354 
363  AllocatorHolder<ALLOC>(std::move(other.get_allocator_ref()))
364  {
365  move(std::move(other));
366  }
367 
376  BasicOptional(BasicOptional&& other, const ALLOC& allocator) :
377  AllocatorHolder<ALLOC>(allocator)
378  {
379  move(std::move(other));
380  }
381 
390  {
391  if (this != &other)
392  {
393  clear();
394  if constexpr (AllocTraits::propagate_on_container_move_assignment::value)
395  {
396  set_allocator(std::move(other.get_allocator_ref()));
397  }
398  move(std::move(other));
399  }
400 
401  return *this;
402  }
403 
409  constexpr bool has_value() const noexcept
410  {
411  if (!m_data.has_value())
412  {
413  return false;
414  }
415  if constexpr (detail::is_optional_heap_allocated_v<T>)
416  {
417  if (!m_data.value())
418  {
419  return false;
420  }
421  }
422  return true;
423  }
424 
428  void reset()
429  {
430  clear();
431  m_data.reset();
432  }
433 
439  void swap(BasicOptional& other)
440  {
441  m_data.swap(other.m_data);
442  if constexpr (AllocTraits::propagate_on_container_swap::value)
443  {
444  using std::swap;
446  }
447  }
448 
454  constexpr explicit operator bool() const noexcept
455  {
456  return has_value();
457  }
458 
466  template <typename... ARGS>
467  T& emplace(ARGS&&... args)
468  {
469  clear();
470  if constexpr (detail::is_optional_heap_allocated_v<T>)
471  {
472  T* ptr = allocateValue(std::forward<ARGS>(args)...);
473  return *m_data.emplace(ptr);
474  }
475  else
476  {
477  return m_data.emplace(std::forward<ARGS>(args)...);
478  }
479  }
480 
488  constexpr T& value()
489  {
490  if (!has_value())
491  {
492  throw BadOptionalAccess("Optional is empty");
493  }
494  if constexpr (detail::is_optional_heap_allocated_v<T>)
495  {
496  return *m_data.value();
497  }
498  else
499  {
500  return m_data.value();
501  }
502  }
503 
511  constexpr const T& value() const
512  {
513  if (!has_value())
514  {
515  throw BadOptionalAccess("Optional is empty");
516  }
517  if constexpr (detail::is_optional_heap_allocated_v<T>)
518  {
519  return *m_data.value();
520  }
521  else
522  {
523  return m_data.value();
524  }
525  }
526 
535  {
536  return value();
537  }
538 
546  const T& operator*() const
547  {
548  return value();
549  }
550 
559  {
560  return &value();
561  }
562 
570  const T* operator->() const
571  {
572  return &value();
573  }
574 
582  template <typename U>
583  T value_or(U&& def)
584  {
585  if (!has_value())
586  {
587  return static_cast<T>(std::forward<U>(def));
588  }
589  return value();
590  }
591 
599  template <typename U>
600  T value_or(U&& def) const
601  {
602  if (!has_value())
603  {
604  return static_cast<T>(std::forward<U>(def));
605  }
606  return value();
607  }
608 
609 private:
610  template <typename... ARGS>
611  T* allocateValue(ARGS&&... args)
612  {
613  using MyAllocType = RebindAlloc<ALLOC, T>;
614  using MyAllocTraits = std::allocator_traits<MyAllocType>;
615  MyAllocType typedAlloc = get_allocator_ref();
616  auto ptr = MyAllocTraits::allocate(typedAlloc, 1);
617  try
618  {
619  MyAllocTraits::construct(typedAlloc, std::addressof(*ptr), std::forward<ARGS>(args)...);
620  }
621  catch (...)
622  {
623  MyAllocTraits::deallocate(typedAlloc, ptr, 1);
624  throw;
625  }
626  return ptr;
627  }
628 
629  // same as for std::Optional throwing dtors are not supported see
630  // https://stackoverflow.com/questions/78602733/should-stdOptional-be-nothrow-destructible-when-its-alternative-has-potentially
631  void destroyValue(T* ptr)
632  {
633  if (ptr)
634  {
635  using MyAllocType = RebindAlloc<ALLOC, T>;
636  using MyAllocTraits = std::allocator_traits<MyAllocType>;
637  MyAllocType typedAlloc = get_allocator_ref();
638  MyAllocTraits::destroy(typedAlloc, ptr);
639  MyAllocTraits::deallocate(typedAlloc, ptr, 1);
640  }
641  }
642 
643  template <typename A, typename U>
644  void copy(const BasicOptional<A, U>& other)
645  {
646  // assumes this holder is cleared
647 
648  if (!other.has_value())
649  {
650  reset();
651  }
652  else
653  {
654  emplace(other.value());
655  }
656  }
657 
658  void move(BasicOptional&& other)
659  {
660  // assumes this holder is cleared
661 
662  if (!other.has_value())
663  {
664  reset();
665  }
666  else
667  {
668  if constexpr (detail::is_optional_heap_allocated_v<T>)
669  {
670  if (get_allocator_ref() == other.get_allocator_ref())
671  {
672  // non-standard optimization to reuse memory from the same resource
673  // note that other will be unset after move in this case
674  auto& ptr = other.m_data.value();
675  m_data.emplace(ptr);
676  ptr = nullptr;
677  }
678  else
679  {
680  auto& value = *other.m_data.value();
681  T* ptr = allocateValue(std::move(value));
682  m_data.emplace(ptr);
683  }
684  }
685  else
686  {
687  auto& value = *other.m_data;
688  m_data.emplace(std::move(value));
689  }
690  }
691  }
692 
693  void clear()
694  {
695  if (!has_value())
696  {
697  return;
698  }
699  if constexpr (detail::is_optional_heap_allocated_v<T>)
700  {
701  auto& ptr = m_data.value();
702  destroyValue(ptr);
703  ptr = nullptr;
704  }
705  }
706 
707  OptionalType m_data;
708 };
709 
710 // Using declarations
711 
712 template <typename T>
714 
723 template <typename ALLOC, typename T>
724 uint32_t calcHashCode(uint32_t seed, const BasicOptional<ALLOC, T>& opt)
725 {
726  uint32_t result = seed;
727  if (opt)
728  {
729  result = calcHashCode(result, *opt);
730  }
731  return result;
732 }
733 
741 template <typename T>
742 constexpr auto make_optional(T&& value)
743 {
744  return Optional<std::decay_t<T>>(std::forward<T>(value));
745 }
746 
754 template <typename T, typename... ARGS>
755 constexpr Optional<T> make_optional(ARGS&&... args)
756 {
757  return Optional<T>(std::in_place, std::forward<ARGS>(args)...);
758 }
759 
768 template <typename A1, typename T, typename A2, typename U>
769 constexpr bool operator==(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
770 {
771  if (first.has_value() && second.has_value())
772  {
773  return first.value() == second.value();
774  }
775  return first.has_value() == second.has_value();
776 }
777 
785 template <typename A, typename T>
786 constexpr bool operator==(const BasicOptional<A, T>& opt, std::nullopt_t)
787 {
788  return !opt.has_value();
789 }
790 
798 template <typename A, typename T>
799 constexpr bool operator==(std::nullopt_t, const BasicOptional<A, T>& opt)
800 {
801  return !opt.has_value();
802 }
803 
812 template <typename A, typename T, typename U>
813 constexpr bool operator==(const BasicOptional<A, T>& opt, const U& value)
814 {
815  return opt.has_value() && opt.value() == value;
816 }
817 
826 template <typename A, typename T, typename U>
827 constexpr bool operator==(const U& value, const BasicOptional<A, T>& opt)
828 {
829  return opt.has_value() && opt.value() == value;
830 }
831 
840 template <typename A1, typename T, typename A2, typename U>
841 constexpr bool operator!=(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
842 {
843  return !(first == second);
844 }
845 
853 template <typename A, typename T>
854 constexpr bool operator!=(const BasicOptional<A, T>& opt, std::nullopt_t)
855 {
856  return !(opt == std::nullopt);
857 }
858 
866 template <typename A, typename T>
867 constexpr bool operator!=(std::nullopt_t, const BasicOptional<A, T>& opt)
868 {
869  return !(std::nullopt == opt);
870 }
871 
880 template <typename A, typename T, typename U>
881 constexpr bool operator!=(const BasicOptional<A, T>& opt, const U& value)
882 {
883  return !(opt == value);
884 }
885 
894 template <typename A, typename T, typename U>
895 constexpr bool operator!=(const U& value, const BasicOptional<A, T>& opt)
896 {
897  return !(value == opt);
898 }
899 
908 template <typename A1, typename T, typename A2, typename U>
909 constexpr bool operator<(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
910 {
911  if (first.has_value() && second.has_value())
912  {
913  return first.value() < second.value();
914  }
915  return first.has_value() < second.has_value();
916 }
917 
925 template <typename A, typename T>
926 constexpr bool operator<(const BasicOptional<A, T>&, std::nullopt_t)
927 {
928  return false;
929 }
930 
938 template <typename A, typename T>
939 constexpr bool operator<(std::nullopt_t, const BasicOptional<A, T>& opt)
940 {
941  return opt.has_value();
942 }
943 
952 template <typename A, typename T, typename U>
953 constexpr bool operator<(const BasicOptional<A, T>& opt, const U& value)
954 {
955  return !opt.has_value() || opt.value() < value;
956 }
957 
966 template <typename A, typename T, typename U>
967 constexpr bool operator<(const U& value, const BasicOptional<A, T>& opt)
968 {
969  return opt.has_value() && value < opt.value();
970 }
971 
980 template <typename A1, typename T, typename A2, typename U>
981 constexpr bool operator>(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
982 {
983  if (first.has_value() && second.has_value())
984  {
985  return first.value() > second.value();
986  }
987  return first.has_value() > second.has_value();
988 }
989 
997 template <typename A, typename T>
998 constexpr bool operator>(const BasicOptional<A, T>& opt, std::nullopt_t)
999 {
1000  return opt.has_value();
1001 }
1002 
1010 template <typename A, typename T>
1011 constexpr bool operator>(std::nullopt_t, const BasicOptional<A, T>&)
1012 {
1013  return false;
1014 }
1015 
1024 template <typename A, typename T, typename U>
1025 constexpr bool operator>(const BasicOptional<A, T>& opt, const U& value)
1026 {
1027  return opt.has_value() && opt.value() > value;
1028 }
1029 
1038 template <typename A, typename T, typename U>
1039 constexpr bool operator>(const U& value, const BasicOptional<A, T>& opt)
1040 {
1041  return !opt.has_value() || value > opt.value();
1042 }
1043 
1052 template <typename A1, typename T, typename A2, typename U>
1053 constexpr bool operator<=(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
1054 {
1055  return !(first > second);
1056 }
1057 
1065 template <typename A, typename T>
1066 constexpr bool operator<=(const BasicOptional<A, T>& opt, std::nullopt_t)
1067 {
1068  return !(opt > std::nullopt);
1069 }
1070 
1078 template <typename A, typename T>
1079 constexpr bool operator<=(std::nullopt_t, const BasicOptional<A, T>& opt)
1080 {
1081  return !(std::nullopt > opt);
1082 }
1083 
1092 template <typename A, typename T, typename U>
1093 constexpr bool operator<=(const BasicOptional<A, T>& opt, const U& value)
1094 {
1095  return !(opt > value);
1096 }
1097 
1106 template <typename A, typename T, typename U>
1107 constexpr bool operator<=(const U& value, const BasicOptional<A, T>& opt)
1108 {
1109  return !(value > opt);
1110 }
1111 
1120 template <typename A1, typename T, typename A2, typename U>
1121 constexpr bool operator>=(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
1122 {
1123  return !(first < second);
1124 }
1125 
1133 template <typename A, typename T>
1134 constexpr bool operator>=(const BasicOptional<A, T>& opt, std::nullopt_t)
1135 {
1136  return !(opt < std::nullopt);
1137 }
1138 
1146 template <typename A, typename T>
1147 constexpr bool operator>=(std::nullopt_t, const BasicOptional<A, T>& opt)
1148 {
1149  return !(std::nullopt < opt);
1150 }
1151 
1160 template <typename A, typename T, typename U>
1161 constexpr bool operator>=(const BasicOptional<A, T>& opt, const U& value)
1162 {
1163  return !(opt < value);
1164 }
1165 
1174 template <typename A, typename T, typename U>
1175 constexpr bool operator>=(const U& value, const BasicOptional<A, T>& opt)
1176 {
1177  return !(value < opt);
1178 }
1179 
1180 } // namespace zserio
1181 
1182 namespace std
1183 {
1184 
1185 template <typename ALLOC, typename T>
1186 struct hash<zserio::BasicOptional<ALLOC, T>>
1187 {
1189  {
1190  return zserio::calcHashCode(zserio::HASH_SEED, opt);
1191  }
1192 };
1193 
1194 } // namespace std
1195 
1196 #endif // ZSERIO_OPTIONAL_H_INC
allocator_type & get_allocator_ref()
void set_allocator(const allocator_type &allocator)
constexpr BasicOptional(const T &value, const ALLOC &allocator={})
Definition: Optional.h:135
constexpr BasicOptional(const ALLOC &allocator)
Definition: Optional.h:154
void swap(BasicOptional &other)
Definition: Optional.h:439
constexpr BasicOptional() noexcept
Definition: Optional.h:118
BasicOptional(BasicOptional &&other)
Definition: Optional.h:362
T value_or(U &&def)
Definition: Optional.h:583
constexpr const T & value() const
Definition: Optional.h:511
T & emplace(ARGS &&... args)
Definition: Optional.h:467
constexpr BasicOptional(const BasicOptional &other, const ALLOC &allocator)
Definition: Optional.h:261
BasicOptional & operator=(std::nullopt_t)
Definition: Optional.h:333
const T * operator->() const
Definition: Optional.h:570
constexpr BasicOptional(std::in_place_t, const ALLOC &allocator, ARGS &&... args)
Definition: Optional.h:184
constexpr BasicOptional(std::in_place_t, ARGS &&... args)
Definition: Optional.h:169
BasicOptional(const BasicOptional< A, U > &other, const ALLOC &allocator)
Definition: Optional.h:276
BasicOptional & operator=(const BasicOptional< A, U > &other)
Definition: Optional.h:316
std::optional< typename detail::optional_element< T >::type > OptionalType
Definition: Optional.h:113
BasicOptional(BasicOptional &&other, const ALLOC &allocator)
Definition: Optional.h:376
constexpr BasicOptional & operator=(const BasicOptional &other)
Definition: Optional.h:291
constexpr BasicOptional(const BasicOptional &other)
Definition: Optional.h:233
T value_or(U &&def) const
Definition: Optional.h:600
constexpr BasicOptional(std::nullopt_t, const ALLOC &allocator={}) noexcept
Definition: Optional.h:125
BasicOptional & operator=(BasicOptional &&other)
Definition: Optional.h:389
constexpr BasicOptional(T &&value, const ALLOC &allocator={})
Definition: Optional.h:145
constexpr bool has_value() const noexcept
Definition: Optional.h:409
constexpr T & value()
Definition: Optional.h:488
constexpr BasicOptional(const BasicOptional< ALLOC, U > &other)
Definition: Optional.h:246
const T & operator*() const
Definition: Optional.h:546
CppRuntimeException(const char *message="")
Definition: BitBuffer.h:602
bool operator>(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition: BitBuffer.h:525
bool operator==(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition: BitBuffer.h:507
typename std::allocator_traits< ALLOC >::template rebind_alloc< T > RebindAlloc
Definition: RebindAlloc.h:10
bool operator<(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition: BitBuffer.h:519
uint32_t calcHashCode(uint32_t seedValue, const ArrayView< T, ARRAY_TRAITS > &array)
Definition: ArrayView.h:860
View(T, ARGS &&...) -> View< T >
constexpr auto make_optional(T &&value)
Definition: Optional.h:742
bool operator<=(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition: BitBuffer.h:531
bool operator>=(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition: BitBuffer.h:537
bool operator!=(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition: BitBuffer.h:513
size_t operator()(const zserio::BasicOptional< ALLOC, T > &opt) const
Definition: Optional.h:1188