mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
For optional monadic operations, add docs, tests, and complete type (#1568)
This commit is contained in:
parent
bfa29188c1
commit
74a95cf360
@ -222,6 +222,64 @@ bool has_value() const
|
|||||||
Checks whether *this is in a valid state, i.e. whether the contained value is initialised.
|
Checks whether *this is in a valid state, i.e. whether the contained value is initialised.
|
||||||
Returns `true` if valid, otherwise `false`.
|
Returns `true` if valid, otherwise `false`.
|
||||||
|
|
||||||
|
## Monadic operations
|
||||||
|
C++11 and above.
|
||||||
|
|
||||||
|
Each operation is supplied as four reference qualified overloads (`&`, `const&`, `&&`, `const&&`),
|
||||||
|
so that the contained value is passed to the supplied callable with the value category of `*this`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template <typename F>
|
||||||
|
etl::optional<U> transform(F&& f)
|
||||||
|
```
|
||||||
|
**Description**
|
||||||
|
If `*this` contains a value, returns an `etl::optional<U>` containing the result of invoking `f`
|
||||||
|
with the contained value, otherwise returns an empty `etl::optional<U>`.
|
||||||
|
|
||||||
|
`U` is the result type of `f`, with any reference and cv qualification removed.
|
||||||
|
|
||||||
|
### Note
|
||||||
|
Unlike `std::optional::transform`, which only removes cv qualification, a callable returning a
|
||||||
|
reference is accepted, and the referenced value is copied into the resulting optional.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template <typename F>
|
||||||
|
U and_then(F&& f)
|
||||||
|
```
|
||||||
|
**Description**
|
||||||
|
If `*this` contains a value, returns the result of invoking `f` with the contained value,
|
||||||
|
otherwise returns an empty `U`.
|
||||||
|
|
||||||
|
`U` is the result type of `f`, with any reference and cv qualification removed, and must be an
|
||||||
|
`etl::optional`. The overload takes no part in overload resolution otherwise.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template <typename F>
|
||||||
|
U or_else(F&& f)
|
||||||
|
```
|
||||||
|
**Description**
|
||||||
|
If `*this` contains a value, returns a copy of `*this`, otherwise returns the result of invoking
|
||||||
|
`f` with no arguments.
|
||||||
|
|
||||||
|
`U` is the result type of `f`, with any reference and cv qualification removed, and must be an
|
||||||
|
`etl::optional` whose `value_type` is `T`. The overload takes no part in overload resolution
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
etl::optional<int> opt = 6;
|
||||||
|
|
||||||
|
etl::optional<double> result = opt.and_then([](int i) { return i > 0 ? etl::optional<int>(i * 2) : etl::optional<int>(); })
|
||||||
|
.or_else([]() { return etl::optional<int>(1); })
|
||||||
|
.transform([](int i) { return double(i) / 2.0; });
|
||||||
|
// result contains 6.0
|
||||||
|
```
|
||||||
|
|
||||||
## Non-member functions
|
## Non-member functions
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@ -269,3 +327,15 @@ operator >=
|
|||||||
```
|
```
|
||||||
**Description**
|
**Description**
|
||||||
`true` if the contents of the lhs are lexicographically greater than or equal to the contents of the rhs, otherwise `false`.
|
`true` if the contents of the lhs are lexicographically greater than or equal to the contents of the rhs, otherwise `false`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template <typename T>
|
||||||
|
struct etl::is_optional;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline constexpr bool etl::is_optional_v; // C++17 and above
|
||||||
|
```
|
||||||
|
**Description**
|
||||||
|
`true` if `T` is an `etl::optional`, otherwise `false`. Any cv qualification on `T` is ignored.
|
||||||
|
|||||||
@ -50,6 +50,11 @@ namespace etl
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class optional;
|
class optional;
|
||||||
|
|
||||||
|
//*****************************************************************************
|
||||||
|
/// Determines if a type is an etl::optional.
|
||||||
|
/// Ignores any cv qualification on the type being tested.
|
||||||
|
///\ingroup utilities
|
||||||
|
//*****************************************************************************
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_optional : etl::false_type
|
struct is_optional : etl::false_type
|
||||||
{
|
{
|
||||||
@ -60,6 +65,26 @@ namespace etl
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_optional<const optional<T> > : etl::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_optional<volatile optional<T> > : etl::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_optional<const volatile optional<T> > : etl::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
#if ETL_USING_CPP17
|
||||||
|
template <typename T>
|
||||||
|
inline constexpr bool is_optional_v = etl::is_optional<T>::value;
|
||||||
|
#endif
|
||||||
|
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
/// A null option type.
|
/// A null option type.
|
||||||
///\ingroup utilities
|
///\ingroup utilities
|
||||||
|
|||||||
@ -1815,6 +1815,137 @@ namespace
|
|||||||
CHECK_TRUE(result_from_empty.has_value());
|
CHECK_TRUE(result_from_empty.has_value());
|
||||||
CHECK_EQUAL(123, result_from_empty.value());
|
CHECK_EQUAL(123, result_from_empty.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_is_optional)
|
||||||
|
{
|
||||||
|
CHECK_TRUE((etl::is_optional<etl::optional<int>>::value));
|
||||||
|
CHECK_TRUE((etl::is_optional<const etl::optional<int>>::value));
|
||||||
|
CHECK_TRUE((etl::is_optional<volatile etl::optional<int>>::value));
|
||||||
|
CHECK_TRUE((etl::is_optional<const volatile etl::optional<int>>::value));
|
||||||
|
CHECK_TRUE((etl::is_optional<etl::optional<Data>>::value));
|
||||||
|
|
||||||
|
CHECK_FALSE((etl::is_optional<int>::value));
|
||||||
|
CHECK_FALSE((etl::is_optional<Data>::value));
|
||||||
|
CHECK_FALSE((etl::is_optional<etl::optional<int>*>::value));
|
||||||
|
CHECK_FALSE((etl::is_optional<etl::optional<int>&>::value));
|
||||||
|
|
||||||
|
#if ETL_USING_CPP17
|
||||||
|
CHECK_TRUE((etl::is_optional_v<etl::optional<int>>));
|
||||||
|
CHECK_TRUE((etl::is_optional_v<const etl::optional<int>>));
|
||||||
|
CHECK_FALSE((etl::is_optional_v<int>));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_transform_const_rvalue)
|
||||||
|
{
|
||||||
|
const etl::optional<int> engaged(42);
|
||||||
|
const etl::optional<int> empty;
|
||||||
|
|
||||||
|
etl::optional<int> doubled = etl::move(engaged).transform([](const int&& i) { return i * 2; });
|
||||||
|
CHECK_TRUE(doubled.has_value());
|
||||||
|
CHECK_EQUAL(84, doubled.value());
|
||||||
|
CHECK_TRUE(engaged.has_value());
|
||||||
|
CHECK_EQUAL(42, engaged.value());
|
||||||
|
|
||||||
|
etl::optional<int> from_empty = etl::move(empty).transform([](const int&& i) { return i * 2; });
|
||||||
|
CHECK_FALSE(from_empty.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_and_then_const_rvalue)
|
||||||
|
{
|
||||||
|
const etl::optional<int> engaged(42);
|
||||||
|
const etl::optional<int> empty;
|
||||||
|
|
||||||
|
etl::optional<int> incremented = etl::move(engaged).and_then([](const int&& i) { return etl::optional<int>(i + 1); });
|
||||||
|
CHECK_TRUE(incremented.has_value());
|
||||||
|
CHECK_EQUAL(43, incremented.value());
|
||||||
|
|
||||||
|
etl::optional<int> from_empty = etl::move(empty).and_then([](const int&& i) { return etl::optional<int>(i + 1); });
|
||||||
|
CHECK_FALSE(from_empty.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_or_else_const_rvalue)
|
||||||
|
{
|
||||||
|
const etl::optional<int> engaged(42);
|
||||||
|
const etl::optional<int> empty;
|
||||||
|
|
||||||
|
etl::optional<int> from_engaged = etl::move(engaged).or_else([]() { return etl::optional<int>(99); });
|
||||||
|
CHECK_TRUE(from_engaged.has_value());
|
||||||
|
CHECK_EQUAL(42, from_engaged.value());
|
||||||
|
|
||||||
|
etl::optional<int> from_empty = etl::move(empty).or_else([]() { return etl::optional<int>(99); });
|
||||||
|
CHECK_TRUE(from_empty.has_value());
|
||||||
|
CHECK_EQUAL(99, from_empty.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Unlike std::optional, a callable returning a reference is accepted.
|
||||||
|
/// The reference is stripped from the result type and the value is copied.
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_transform_callable_returning_reference)
|
||||||
|
{
|
||||||
|
etl::optional<int> engaged(42);
|
||||||
|
|
||||||
|
auto result = engaged.transform([](int& i) -> int& { return i; });
|
||||||
|
|
||||||
|
CHECK_TRUE((etl::is_same<etl::optional<int>, decltype(result)>::value));
|
||||||
|
CHECK_TRUE(result.has_value());
|
||||||
|
CHECK_EQUAL(42, result.value());
|
||||||
|
|
||||||
|
// The result is a copy, not an alias of the source.
|
||||||
|
result.value() = 1;
|
||||||
|
CHECK_EQUAL(42, engaged.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_and_then_callable_returning_reference)
|
||||||
|
{
|
||||||
|
etl::optional<int> engaged(42);
|
||||||
|
etl::optional<int> empty;
|
||||||
|
|
||||||
|
auto f = [](int i) -> etl::optional<int>&
|
||||||
|
{
|
||||||
|
static etl::optional<int> shared;
|
||||||
|
shared = i + 1;
|
||||||
|
return shared;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = engaged.and_then(f);
|
||||||
|
|
||||||
|
CHECK_TRUE((etl::is_same<etl::optional<int>, decltype(result)>::value));
|
||||||
|
CHECK_TRUE(result.has_value());
|
||||||
|
CHECK_EQUAL(43, result.value());
|
||||||
|
|
||||||
|
auto from_empty = empty.and_then(f);
|
||||||
|
CHECK_FALSE(from_empty.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_or_else_callable_returning_reference)
|
||||||
|
{
|
||||||
|
etl::optional<int> engaged(42);
|
||||||
|
etl::optional<int> empty;
|
||||||
|
|
||||||
|
auto f = []() -> etl::optional<int>&
|
||||||
|
{
|
||||||
|
static etl::optional<int> shared(99);
|
||||||
|
return shared;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto from_engaged = engaged.or_else(f);
|
||||||
|
|
||||||
|
CHECK_TRUE((etl::is_same<etl::optional<int>, decltype(from_engaged)>::value));
|
||||||
|
CHECK_TRUE(from_engaged.has_value());
|
||||||
|
CHECK_EQUAL(42, from_engaged.value());
|
||||||
|
|
||||||
|
auto from_empty = empty.or_else(f);
|
||||||
|
CHECK_TRUE(from_empty.has_value());
|
||||||
|
CHECK_EQUAL(99, from_empty.value());
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user