Zserio C++17 runtime library 1.3.0
Built for Zserio 2.20.0
Loading...
Searching...
No Matches
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
9#include "zserio/Traits.h"
10#include "zserio/View.h"
11
12namespace zserio
13{
14
15template <typename ALLOC, typename T>
16class BasicOptional;
17
18namespace detail
19{
20
21template <typename T, bool = (sizeof(T) > 3 * sizeof(void*))>
22struct is_optional_big : std::false_type
23{};
24
25template <typename T>
26struct is_optional_big<T, true> : std::true_type
27{};
28
29template <typename T, typename = void>
30struct is_recursive : std::false_type
31{};
32
33template <typename T>
34struct is_recursive<T, std::void_t<typename T::IS_RECURSIVE>> : std::true_type
35{};
36
37template <typename T, bool = is_recursive<T>::value>
38struct is_optional_heap_allocated_impl : is_optional_big<T>::type
39{};
40
41template <typename T>
42struct is_optional_heap_allocated_impl<T, true> : std::true_type
43{};
44
45template <typename T>
46struct is_optional_heap_allocated : is_optional_heap_allocated_impl<T>
47{};
48
49template <typename T>
50struct is_optional_heap_allocated<View<T>> : std::false_type
51{};
52
53template <typename T>
54constexpr bool is_optional_heap_allocated_v = is_optional_heap_allocated<T>::value;
55
56template <typename T, bool heap = is_optional_heap_allocated_v<T>>
57struct optional_element
58{
59 using type = T;
60};
61
62template <typename T>
63struct optional_element<T, true>
64{
65 using type = T*;
66};
67
68template <typename T>
69struct is_optional : std::false_type
70{};
71
72template <typename A, typename T>
73struct is_optional<BasicOptional<A, T>> : std::true_type
74{};
75
76template <typename T>
77constexpr bool is_optional_v = is_optional<T>::value;
78
79} // namespace detail
80
91
106template <typename ALLOC, typename T>
107class BasicOptional : public AllocatorHolder<ALLOC>
108{
109 using AllocTraits = std::allocator_traits<ALLOC>;
111
112public:
115 using OptionalType = std::optional<typename detail::optional_element<T>::type>;
116
122 {}
123
127 constexpr BasicOptional(std::nullopt_t, const ALLOC& allocator = {}) noexcept :
129 {}
130
137 constexpr BasicOptional(const T& value, const ALLOC& allocator = {}) :
139 {}
140
147 constexpr BasicOptional(T&& value, const ALLOC& allocator = {}) :
149 {}
150
156 explicit constexpr BasicOptional(const ALLOC& allocator) :
158 {}
159
168 template <typename... ARGS, typename TT = T,
169 std::enable_if_t<!detail::is_optional_heap_allocated_v<TT>>* = nullptr,
170 std::enable_if_t<!is_first_allocator<ARGS...>::value>* = nullptr>
171 explicit constexpr BasicOptional(std::in_place_t, ARGS&&... args) :
172 m_data(std::in_place, std::forward<ARGS>(args)...)
173 {}
174
184 template <typename... ARGS, typename TT = T,
185 std::enable_if_t<!detail::is_optional_heap_allocated_v<TT>>* = nullptr>
186 constexpr BasicOptional(std::in_place_t, const ALLOC& allocator, ARGS&&... args) :
188 m_data(std::in_place, std::forward<ARGS>(args)...)
189 {}
190
200 template <typename... ARGS, typename TT = T,
201 std::enable_if_t<detail::is_optional_heap_allocated_v<TT>>* = nullptr>
202 constexpr BasicOptional(std::in_place_t, const ALLOC& allocator, ARGS&&... args) :
204 m_data(std::in_place, allocateValue(std::forward<ARGS>(args)...))
205 {}
206
215 template <typename... ARGS, typename TT = T,
216 std::enable_if_t<detail::is_optional_heap_allocated_v<TT>>* = nullptr,
217 std::enable_if_t<!is_first_allocator<ARGS...>::value>* = nullptr>
218 explicit constexpr BasicOptional(std::in_place_t, ARGS&&... args) :
219 m_data(std::in_place, allocateValue(std::forward<ARGS>(args)...))
220 {}
221
226 {
227 clear();
228 }
229
237 AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
238 {
239 copy(other);
240 }
241
247 template <typename U>
250 AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
251 {
252 copy(other);
253 }
254
263 constexpr BasicOptional(const BasicOptional& other, const ALLOC& allocator) :
265 {
266 copy(other);
267 }
268
277 template <typename A, typename U>
280 {
281 copy(other);
282 }
283
294 {
295 if (this != &other)
296 {
297 clear();
298 if constexpr (AllocTraits::propagate_on_container_copy_assignment::value)
299 {
300 set_allocator(other.get_allocator_ref());
301 }
302 copy(other);
303 }
304
305 return *this;
306 }
307
317 template <typename A, typename U>
319 {
320 clear();
321 if constexpr (AllocTraits::propagate_on_container_copy_assignment::value)
322 {
323 set_allocator(other.get_allocator_ref());
324 }
325 copy(other);
326
327 return *this;
328 }
329
335 BasicOptional& operator=(std::nullopt_t)
336 {
337 reset();
338 return *this;
339 }
340
348 template <typename U = T,
349 std::enable_if_t<!detail::is_optional_v<std::decay_t<U>> &&
350 !(std::is_same_v<T, std::decay_t<U>> && std::is_scalar_v<U>)>* = nullptr>
351 BasicOptional& operator=(U&& value)
352 {
353 emplace(std::forward<U>(value));
354 return *this;
355 }
356
366 {
367 move(std::move(other));
368 }
369
380 {
381 move(std::move(other));
382 }
383
392 {
393 if (this != &other)
394 {
395 clear();
396 if constexpr (AllocTraits::propagate_on_container_move_assignment::value)
397 {
398 set_allocator(std::move(other.get_allocator_ref()));
399 }
400 move(std::move(other));
401 }
402
403 return *this;
404 }
405
411 template <typename U>
414 {
415 move(std::move(other));
416 }
417
424 template <typename U>
427 {
428 move(std::move(other));
429 }
430
438 template <typename U>
440 {
441 static_assert(!std::is_same_v<T, U>);
442
443 clear();
444 if constexpr (AllocTraits::propagate_on_container_move_assignment::value)
445 {
446 set_allocator(std::move(other.get_allocator_ref()));
447 }
448 move(std::move(other));
449 return *this;
450 }
451
457 constexpr bool has_value() const noexcept
458 {
459 if (!m_data.has_value())
460 {
461 return false;
462 }
463 if constexpr (detail::is_optional_heap_allocated_v<T>)
464 {
465 if (!m_data.value())
466 {
467 return false;
468 }
469 }
470 return true;
471 }
472
476 void reset()
477 {
478 clear();
479 m_data.reset();
480 }
481
488 {
489 m_data.swap(other.m_data);
490 if constexpr (AllocTraits::propagate_on_container_swap::value)
491 {
492 using std::swap;
493 swap(get_allocator_ref(), other.get_allocator_ref());
494 }
495 }
496
502 constexpr explicit operator bool() const noexcept
503 {
504 return has_value();
505 }
506
514 template <typename... ARGS>
516 {
517 clear();
518 if constexpr (detail::is_optional_heap_allocated_v<T>)
519 {
520 T* ptr = allocateValue(std::forward<ARGS>(args)...);
521 return *m_data.emplace(ptr);
522 }
523 else
524 {
525 return m_data.emplace(std::forward<ARGS>(args)...);
526 }
527 }
528
536 constexpr T& value() &
537 {
538 if (!has_value())
539 {
540 throw BadOptionalAccess("Optional is empty");
541 }
542 if constexpr (detail::is_optional_heap_allocated_v<T>)
543 {
544 return *m_data.value();
545 }
546 else
547 {
548 return m_data.value();
549 }
550 }
551
559 constexpr T&& value() &&
560 {
561 return std::move(value());
562 }
563
571 constexpr const T& value() const&
572 {
573 if (!has_value())
574 {
575 throw BadOptionalAccess("Optional is empty");
576 }
577 if constexpr (detail::is_optional_heap_allocated_v<T>)
578 {
579 return *m_data.value();
580 }
581 else
582 {
583 return m_data.value();
584 }
585 }
586
594 constexpr const T&& value() const&&
595 {
596 return std::move(value());
597 }
598
607 {
608 return value();
609 }
610
618 T&& operator*() &&
619 {
620 return std::move(value());
621 }
622
630 const T& operator*() const&
631 {
632 return value();
633 }
634
642 const T&& operator*() const&&
643 {
644 return std::move(value());
645 }
646
655 {
656 return &value();
657 }
658
666 const T* operator->() const
667 {
668 return &value();
669 }
670
678 template <typename U>
680 {
681 if (!has_value())
682 {
683 return static_cast<T>(std::forward<U>(def));
684 }
685 return value();
686 }
687
695 template <typename U>
696 T value_or(U&& def) const
697 {
698 if (!has_value())
699 {
700 return static_cast<T>(std::forward<U>(def));
701 }
702 return value();
703 }
704
705private:
706 // enables move(BasicOptional<ALLOC, U>&&) implementation to access m_data
707 template <typename A, typename U>
708 friend class BasicOptional;
709
710 template <typename... ARGS>
711 T* allocateValue(ARGS&&... args)
712 {
714 using MyAllocTraits = std::allocator_traits<MyAllocType>;
716 auto ptr = MyAllocTraits::allocate(typedAlloc, 1);
717 try
718 {
719 MyAllocTraits::construct(typedAlloc, std::addressof(*ptr), std::forward<ARGS>(args)...);
720 }
721 catch (...)
722 {
723 MyAllocTraits::deallocate(typedAlloc, ptr, 1);
724 throw;
725 }
726 return ptr;
727 }
728
729 // same as for std::Optional throwing dtors are not supported see
730 // https://stackoverflow.com/questions/78602733/should-stdOptional-be-nothrow-destructible-when-its-alternative-has-potentially
731 void destroyValue(T* ptr)
732 {
733 if (ptr)
734 {
735 using MyAllocType = RebindAlloc<ALLOC, T>;
736 using MyAllocTraits = std::allocator_traits<MyAllocType>;
737 MyAllocType typedAlloc = get_allocator_ref();
738 MyAllocTraits::destroy(typedAlloc, ptr);
739 MyAllocTraits::deallocate(typedAlloc, ptr, 1);
740 }
741 }
742
743 template <typename A, typename U>
744 void copy(const BasicOptional<A, U>& other)
745 {
746 // assumes this holder is cleared
747
748 if (!other.has_value())
749 {
750 reset();
751 }
752 else
753 {
754 emplace(other.value());
755 }
756 }
757
758 void move(BasicOptional&& other)
759 {
760 // assumes this holder is cleared
761
762 if (!other.has_value())
763 {
764 reset();
765 }
766 else
767 {
768 if constexpr (detail::is_optional_heap_allocated_v<T>)
769 {
770 if (get_allocator_ref() == other.get_allocator_ref())
771 {
772 // non-standard optimization to reuse memory from the same resource
773 // note that other will be unset after move in this case
774 auto& ptr = other.m_data.value();
775 m_data.emplace(ptr);
776 ptr = nullptr;
777 }
778 else
779 {
780 auto& value = *other.m_data.value();
781 T* ptr = allocateValue(std::move(value));
782 m_data.emplace(ptr);
783 }
784 }
785 else // not on heap
786 {
787 auto& value = other.m_data.value();
788 m_data.emplace(std::move(value));
789 }
790 }
791 }
792
793 template <typename U>
794 void move(BasicOptional<ALLOC, U>&& other)
795 {
796 // assumes this holder is cleared
797 static_assert(!std::is_same_v<T, U>);
798
799 if (!other.has_value())
800 {
801 reset();
802 }
803 else
804 {
805 if constexpr (detail::is_optional_heap_allocated_v<T> && detail::is_optional_heap_allocated_v<U>)
806 {
807 auto& value = *other.m_data.value();
808 T* ptr = allocateValue(std::move(value));
809 m_data.emplace(ptr);
810 }
811 else if constexpr (detail::is_optional_heap_allocated_v<T>) // only T on heap
812 {
813 auto& value = other.m_data.value();
814 T* ptr = allocateValue(std::move(value));
815 m_data.emplace(ptr);
816 }
817 else if constexpr (detail::is_optional_heap_allocated_v<U>) // only U is on heap
818 {
819 auto& value = *other.m_data.value();
820 m_data.emplace(std::move(value));
821 }
822 else // both not on heap
823 {
824 auto& value = other.m_data.value();
825 m_data.emplace(std::move(value));
826 }
827 }
828 }
829
830 void clear()
831 {
832 if (!has_value())
833 {
834 return;
835 }
836 if constexpr (detail::is_optional_heap_allocated_v<T>)
837 {
838 auto& ptr = m_data.value();
839 destroyValue(ptr);
840 ptr = nullptr;
841 }
842 }
843
844 OptionalType m_data;
845};
846
847// Using declarations
848
849template <typename T>
851
860template <typename ALLOC, typename T>
862{
864 if (opt)
865 {
867 }
868 return result;
869}
870
878template <typename T>
879constexpr auto make_optional(T&& value)
880{
881 return Optional<std::decay_t<T>>(std::forward<T>(value));
882}
883
891template <typename T, typename... ARGS>
893{
894 return Optional<T>(std::in_place, std::forward<ARGS>(args)...);
895}
896
905template <typename A1, typename T, typename A2, typename U>
906constexpr bool operator==(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
907{
908 if (first.has_value() && second.has_value())
909 {
910 return first.value() == second.value();
911 }
912 return first.has_value() == second.has_value();
913}
914
922template <typename A, typename T>
923constexpr bool operator==(const BasicOptional<A, T>& opt, std::nullopt_t)
924{
925 return !opt.has_value();
926}
927
935template <typename A, typename T>
936constexpr bool operator==(std::nullopt_t, const BasicOptional<A, T>& opt)
937{
938 return !opt.has_value();
939}
940
949template <typename A, typename T, typename U>
950constexpr bool operator==(const BasicOptional<A, T>& opt, const U& value)
951{
952 return opt.has_value() && opt.value() == value;
953}
954
963template <typename A, typename T, typename U>
964constexpr bool operator==(const U& value, const BasicOptional<A, T>& opt)
965{
966 return opt.has_value() && opt.value() == value;
967}
968
977template <typename A1, typename T, typename A2, typename U>
978constexpr bool operator!=(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
979{
980 return !(first == second);
981}
982
990template <typename A, typename T>
991constexpr bool operator!=(const BasicOptional<A, T>& opt, std::nullopt_t)
992{
993 return !(opt == std::nullopt);
994}
995
1003template <typename A, typename T>
1004constexpr bool operator!=(std::nullopt_t, const BasicOptional<A, T>& opt)
1005{
1006 return !(std::nullopt == opt);
1007}
1008
1017template <typename A, typename T, typename U>
1018constexpr bool operator!=(const BasicOptional<A, T>& opt, const U& value)
1019{
1020 return !(opt == value);
1021}
1022
1031template <typename A, typename T, typename U>
1032constexpr bool operator!=(const U& value, const BasicOptional<A, T>& opt)
1033{
1034 return !(value == opt);
1035}
1036
1045template <typename A1, typename T, typename A2, typename U>
1046constexpr bool operator<(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
1047{
1048 if (first.has_value() && second.has_value())
1049 {
1050 return first.value() < second.value();
1051 }
1052 return first.has_value() < second.has_value();
1053}
1054
1062template <typename A, typename T>
1063constexpr bool operator<(const BasicOptional<A, T>&, std::nullopt_t)
1064{
1065 return false;
1066}
1067
1075template <typename A, typename T>
1076constexpr bool operator<(std::nullopt_t, const BasicOptional<A, T>& opt)
1077{
1078 return opt.has_value();
1079}
1080
1089template <typename A, typename T, typename U>
1090constexpr bool operator<(const BasicOptional<A, T>& opt, const U& value)
1091{
1092 return !opt.has_value() || opt.value() < value;
1093}
1094
1103template <typename A, typename T, typename U>
1104constexpr bool operator<(const U& value, const BasicOptional<A, T>& opt)
1105{
1106 return opt.has_value() && value < opt.value();
1107}
1108
1117template <typename A1, typename T, typename A2, typename U>
1118constexpr bool operator>(const BasicOptional<A1, T>& first, const BasicOptional<A2, U>& second)
1119{
1120 if (first.has_value() && second.has_value())
1121 {
1122 return first.value() > second.value();
1123 }
1124 return first.has_value() > second.has_value();
1125}
1126
1134template <typename A, typename T>
1135constexpr bool operator>(const BasicOptional<A, T>& opt, std::nullopt_t)
1136{
1137 return opt.has_value();
1138}
1139
1147template <typename A, typename T>
1148constexpr bool operator>(std::nullopt_t, const BasicOptional<A, T>&)
1149{
1150 return false;
1151}
1152
1161template <typename A, typename T, typename U>
1162constexpr bool operator>(const BasicOptional<A, T>& opt, const U& value)
1163{
1164 return opt.has_value() && opt.value() > value;
1165}
1166
1175template <typename A, typename T, typename U>
1176constexpr bool operator>(const U& value, const BasicOptional<A, T>& opt)
1177{
1178 return !opt.has_value() || value > opt.value();
1179}
1180
1189template <typename A1, typename T, typename A2, typename U>
1191{
1192 return !(first > second);
1193}
1194
1202template <typename A, typename T>
1203constexpr bool operator<=(const BasicOptional<A, T>& opt, std::nullopt_t)
1204{
1205 return !(opt > std::nullopt);
1206}
1207
1215template <typename A, typename T>
1216constexpr bool operator<=(std::nullopt_t, const BasicOptional<A, T>& opt)
1217{
1218 return !(std::nullopt > opt);
1219}
1220
1229template <typename A, typename T, typename U>
1230constexpr bool operator<=(const BasicOptional<A, T>& opt, const U& value)
1231{
1232 return !(opt > value);
1233}
1234
1243template <typename A, typename T, typename U>
1244constexpr bool operator<=(const U& value, const BasicOptional<A, T>& opt)
1245{
1246 return !(value > opt);
1247}
1248
1257template <typename A1, typename T, typename A2, typename U>
1259{
1260 return !(first < second);
1261}
1262
1270template <typename A, typename T>
1271constexpr bool operator>=(const BasicOptional<A, T>& opt, std::nullopt_t)
1272{
1273 return !(opt < std::nullopt);
1274}
1275
1283template <typename A, typename T>
1284constexpr bool operator>=(std::nullopt_t, const BasicOptional<A, T>& opt)
1285{
1286 return !(std::nullopt < opt);
1287}
1288
1297template <typename A, typename T, typename U>
1298constexpr bool operator>=(const BasicOptional<A, T>& opt, const U& value)
1299{
1300 return !(opt < value);
1301}
1302
1311template <typename A, typename T, typename U>
1312constexpr bool operator>=(const U& value, const BasicOptional<A, T>& opt)
1313{
1314 return !(value < opt);
1315}
1316
1317} // namespace zserio
1318
1319namespace std
1320{
1321
1322template <typename ALLOC, typename T>
1323struct hash<zserio::BasicOptional<ALLOC, T>>
1324{
1325 size_t operator()(const zserio::BasicOptional<ALLOC, T>& opt) const
1326 {
1327 return zserio::calcHashCode(zserio::HASH_SEED, opt);
1328 }
1329};
1330
1331} // namespace std
1332
1333#endif // ZSERIO_OPTIONAL_H_INC
void set_allocator(const allocator_type &allocator)
allocator_type & get_allocator_ref()
BasicOptional & operator=(BasicOptional< ALLOC, U > &&other)
Definition Optional.h:439
constexpr BasicOptional(BasicOptional< ALLOC, U > &&other, const ALLOC &allocator)
Definition Optional.h:425
constexpr BasicOptional(const T &value, const ALLOC &allocator={})
Definition Optional.h:137
constexpr BasicOptional(const ALLOC &allocator)
Definition Optional.h:156
constexpr BasicOptional & operator=(const BasicOptional &other)
Definition Optional.h:293
void swap(BasicOptional &other)
Definition Optional.h:487
const T * operator->() const
Definition Optional.h:666
constexpr BasicOptional() noexcept
Definition Optional.h:120
BasicOptional(BasicOptional &&other)
Definition Optional.h:364
BasicOptional & operator=(std::nullopt_t)
Definition Optional.h:335
T value_or(U &&def)
Definition Optional.h:679
constexpr T && value() &&
Definition Optional.h:559
constexpr BasicOptional(BasicOptional< ALLOC, U > &&other)
Definition Optional.h:412
constexpr BasicOptional(const BasicOptional &other, const ALLOC &allocator)
Definition Optional.h:263
const T && operator*() const &&
Definition Optional.h:642
constexpr BasicOptional(std::in_place_t, const ALLOC &allocator, ARGS &&... args)
Definition Optional.h:186
constexpr BasicOptional(std::in_place_t, ARGS &&... args)
Definition Optional.h:171
std::optional< typename detail::optional_element< T >::type > OptionalType
Definition Optional.h:115
BasicOptional(BasicOptional &&other, const ALLOC &allocator)
Definition Optional.h:378
friend class BasicOptional
Definition Optional.h:708
constexpr const T & value() const &
Definition Optional.h:571
constexpr BasicOptional(const BasicOptional &other)
Definition Optional.h:235
T & emplace(ARGS &&... args)
Definition Optional.h:515
T value_or(U &&def) const
Definition Optional.h:696
const T & operator*() const &
Definition Optional.h:630
constexpr BasicOptional(const BasicOptional< A, U > &other, const ALLOC &allocator)
Definition Optional.h:278
constexpr BasicOptional(std::nullopt_t, const ALLOC &allocator={}) noexcept
Definition Optional.h:127
BasicOptional & operator=(const BasicOptional< A, U > &other)
Definition Optional.h:318
constexpr BasicOptional(T &&value, const ALLOC &allocator={})
Definition Optional.h:147
constexpr bool has_value() const noexcept
Definition Optional.h:457
constexpr T & value() &
Definition Optional.h:536
BasicOptional & operator=(BasicOptional &&other)
Definition Optional.h:391
constexpr const T && value() const &&
Definition Optional.h:594
constexpr BasicOptional(const BasicOptional< ALLOC, U > &other)
Definition Optional.h:248
CppRuntimeException(const char *message="")
bool operator>(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition BitBuffer.h:526
bool operator==(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition BitBuffer.h:508
bool operator<(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition BitBuffer.h:520
uint32_t calcHashCode(uint32_t seedValue, const ArrayView< T, ARRAY_TRAITS > &array)
Definition ArrayView.h:882
View(T, ARGS &&...) -> View< T >
constexpr auto make_optional(T &&value)
Definition Optional.h:879
bool operator<=(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition BitBuffer.h:532
bool operator>=(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition BitBuffer.h:538
bool operator!=(const BasicBitBufferView< ALLOC > &lhs, const BasicBitBufferView< ALLOC > &rhs)
Definition BitBuffer.h:514