Line | Branch | Exec | Source |
---|---|---|---|
1 | #pragma once | ||
2 | |||
3 | #include <type_traits> | ||
4 | #include <optional> | ||
5 | |||
6 | namespace CXXIter { | ||
7 | |||
8 | // ################################################################################################ | ||
9 | // ITERATOR OPTIONAL (supports references) | ||
10 | // ################################################################################################ | ||
11 | |||
12 | /** | ||
13 | * @brief Container that is used to pass elements through CXXIter's iterator pipelines. | ||
14 | * @details This is essentially a @c std::optional<> that also transparently supports references (in comparison | ||
15 | * to the original). This is achieved by wrapping reference types in a @c std::reference_wrapper<> before storing | ||
16 | * them in the internal @c std::optional<>. | ||
17 | */ | ||
18 | template<typename TValue> | ||
19 | class IterValue { | ||
20 | private: | ||
21 | /** Owned (references removed) version of the type that is to be stored in this IterValue. */ | ||
22 | using TValueDeref = std::remove_reference_t<TValue>; | ||
23 | using TValueStore = std::conditional_t< | ||
24 | std::is_reference_v<TValue>, | ||
25 | std::reference_wrapper<TValueDeref>, | ||
26 | TValue | ||
27 | >; | ||
28 | |||
29 | std::optional<TValueStore> inner; | ||
30 | |||
31 | public: | ||
32 | /** ctor */ | ||
33 | 1188 | constexpr IterValue() noexcept {} | |
34 | /** ctor */ | ||
35 | 2200 | constexpr IterValue(TValue value) noexcept requires std::is_reference_v<TValue> : inner(value) {} | |
36 | /** ctor */ | ||
37 | 2278 | constexpr IterValue(const TValueDeref& value) noexcept(std::is_nothrow_copy_constructible_v<TValueDeref>) | |
38 | 2278 | requires (!std::is_reference_v<TValue>) : inner(value) {} | |
39 | /** ctor */ | ||
40 | 1506 | constexpr IterValue(TValueDeref&& value) noexcept(std::is_nothrow_move_constructible_v<TValueDeref>) | |
41 | 1506 | : inner(std::forward<TValueDeref>(value)) {} | |
42 | |||
43 | /** Assignment from another IterValue instance. */ | ||
44 | 1034 | constexpr IterValue& operator=(IterValue&& o) = default; | |
45 | /** Assignment from another IterValue instance. */ | ||
46 | 2902 | constexpr IterValue(IterValue&& o) noexcept(std::is_nothrow_move_constructible_v<TValueDeref>) | |
47 | 2902 | : inner(std::move(o.inner)) { | |
48 | 2902 | o.inner.reset(); | |
49 | 2902 | }; | |
50 | |||
51 | /** Assignment from an instance of the stored type. */ | ||
52 | constexpr auto& operator=(TValueDeref&& o) { | ||
53 | this->inner = std::forward<decltype(o)>(o); | ||
54 | return *this; | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * @brief Get the contained value (if any). | ||
59 | * @throws If this is called when no value is contained. | ||
60 | * @return const reference to the contained value. | ||
61 | */ | ||
62 | constexpr inline const TValueDeref& value() const { return inner.value(); } | ||
63 | /** | ||
64 | * @brief Get the contained value (if any). | ||
65 | * @throws If this is called when no value is contained. | ||
66 | * @return reference to the contained value. | ||
67 | */ | ||
68 | 6250 | constexpr inline TValueDeref& value() { return inner.value(); } | |
69 | /** | ||
70 | * @brief Get the contained value, or alternatively the given @p def if none is present. | ||
71 | * @param def Default value to return when this optional does not contain a value. | ||
72 | * @return const reference to the contained value (if any), or alternatively the given @p def value. | ||
73 | */ | ||
74 | constexpr inline const TValueDeref& value_or(TValueDeref&& def) const noexcept { return inner.value_or(def); } | ||
75 | /** | ||
76 | * @brief Get the contained value, or alternatively the given @p def if none is present. | ||
77 | * @param def Default value to return when this optional does not contain a value. | ||
78 | * @return reference to the contained value (if any), or alternatively the given @p def value. | ||
79 | */ | ||
80 | constexpr inline TValueDeref& value_or(TValueDeref&& def) noexcept { return inner.value_or(def); } | ||
81 | |||
82 | /** | ||
83 | * @brief Get whether this optional IteratorValue contains a value. | ||
84 | * @return @c true when this IterValue contains a value, @c false otherwise. | ||
85 | */ | ||
86 | 9742 | constexpr bool has_value() const noexcept { return inner.has_value(); } | |
87 | |||
88 | /** | ||
89 | * @brief Swap the values within this and another IterValue container. | ||
90 | * @param o Other IterValue container to swap contents with. | ||
91 | */ | ||
92 | 28 | constexpr void swap(IterValue<TValue>& o) noexcept { inner.swap(o.inner); } | |
93 | |||
94 | /** | ||
95 | * @brief Convert this IterValue to a @c std::optional<> on the owned (no-reference) type. | ||
96 | * @details If the contained value is a reference, the value will be copied into the returned @c std::optional<>. | ||
97 | * @return @c std::optional<> containing an owned version of the value contained by this IterValue. | ||
98 | */ | ||
99 | 34 | constexpr std::optional<TValueStore> toStdOptional() noexcept { | |
100 | 34 | return std::optional<TValueStore>(std::move(this->inner)); | |
101 | } | ||
102 | /** | ||
103 | * @brief Cast to @c std::optional<>. | ||
104 | */ | ||
105 | constexpr operator std::optional<TValueStore>() noexcept { return toStdOptional(); } | ||
106 | |||
107 | /** | ||
108 | * @brief Map this IterValue's contained value (if any) to the requested new @p TOutValue type, using the given @p mapFn | ||
109 | * @tparam The requested output type for the mapping operation. | ||
110 | * @param mapFn Mapper function that takes this IterValue's contained value (if any) to map it to an instance of @p TOutValue | ||
111 | * @return A new IterValue containing the mapped value if this instance contained a value. | ||
112 | */ | ||
113 | template<typename TOutValue, std::invocable<TValueDeref&&> TMapFn> | ||
114 | requires (!std::is_reference_v<TValue>) | ||
115 | 108 | constexpr IterValue<TOutValue> map(TMapFn mapFn) { | |
116 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 60 times.
|
108 | if(!has_value()) { return {}; } |
117 |
1/2✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
84 | return mapFn(std::forward<TValueDeref>(value())); |
118 | } | ||
119 | |||
120 | /** | ||
121 | * @brief Map this IterValue's contained value (if any) to the requested new @p TOutValue type, using the given @p mapFn | ||
122 | * @tparam The requested output type for the mapping operation. | ||
123 | * @param mapFn Mapper function that takes this IterValue's contained value (if any) to map it to an instance of @p TOutValue | ||
124 | * @return A new IterValue containing the mapped value if this instance contained a value. | ||
125 | */ | ||
126 | template<typename TOutValue, std::invocable<TValue> TMapFn> | ||
127 | requires std::is_reference_v<TValue> | ||
128 | 454 | constexpr IterValue<TOutValue> map(TMapFn mapFn) { | |
129 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
|
454 | if(!this->has_value()) { return {}; } |
130 | 383 | return mapFn(this->value()); | |
131 | } | ||
132 | }; | ||
133 | |||
134 | } | ||
135 |