| 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 |