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.
|
||||
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
|
||||
|
||||
```cpp
|
||||
@ -269,3 +327,15 @@ operator >=
|
||||
```
|
||||
**Description**
|
||||
`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>
|
||||
class optional;
|
||||
|
||||
//*****************************************************************************
|
||||
/// Determines if a type is an etl::optional.
|
||||
/// Ignores any cv qualification on the type being tested.
|
||||
///\ingroup utilities
|
||||
//*****************************************************************************
|
||||
template <typename T>
|
||||
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.
|
||||
///\ingroup utilities
|
||||
|
||||
@ -1815,6 +1815,137 @@ namespace
|
||||
CHECK_TRUE(result_from_empty.has_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
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user