summaryrefslogtreecommitdiff
path: root/asl
diff options
context:
space:
mode:
authorSteven Le Rouzic <steven.lerouzic@gmail.com>2024-11-02 20:08:07 +0100
committerSteven Le Rouzic <steven.lerouzic@gmail.com>2024-12-20 15:35:58 +0100
commit7e8e2ef0be2eebc7f872ea37620dc1b60d62197f (patch)
tree850b4a6656e976fd2dd91f5b3624ce29d3653b71 /asl
parentc09323804c6c6d42c052b1c60061134261e515fc (diff)
Add option::and_then
Diffstat (limited to 'asl')
-rw-r--r--asl/option.hpp37
-rw-r--r--asl/tests/option_tests.cpp31
2 files changed, 59 insertions, 9 deletions
diff --git a/asl/option.hpp b/asl/option.hpp
index ab88992..ebb9eef 100644
--- a/asl/option.hpp
+++ b/asl/option.hpp
@@ -3,6 +3,7 @@
#include "asl/assert.hpp"
#include "asl/meta.hpp"
#include "asl/maybe_uninit.hpp"
+#include "asl/functional.hpp"
namespace asl
{
@@ -334,7 +335,6 @@ public:
constexpr bool has_value() const { return m_has_value; }
- // @Todo Do we want this on rvalues? Or maybe some kind of unwrap?
constexpr T&& value() &&
{
ASL_ASSERT(m_has_value); // @Todo Release assert
@@ -389,13 +389,46 @@ public:
}
template<typename... Args>
- T& emplace(Args&&... args) &
+ constexpr T& emplace(Args&&... args) &
requires constructible<T, Args&&...>
{
if (m_has_value) { reset(); }
construct(ASL_FWD(args)...);
return value();
}
+
+ template<typename F>
+ constexpr auto and_then(F&& f) &
+ requires is_option<result_of_t<F(T&)>>
+ {
+ if (has_value())
+ {
+ return invoke(ASL_FWD(f), value());
+ }
+ return un_cvref_t<result_of_t<F(T&)>>{};
+ }
+
+ template<typename F>
+ constexpr auto and_then(F&& f) const&
+ requires is_option<result_of_t<F(const T&)>>
+ {
+ if (has_value())
+ {
+ return invoke(ASL_FWD(f), value());
+ }
+ return un_cvref_t<result_of_t<F(const T&)>>{};
+ }
+
+ template<typename F>
+ constexpr auto and_then(F&& f) &&
+ requires is_option<result_of_t<F(T)>>
+ {
+ if (has_value())
+ {
+ return invoke(ASL_FWD(f), ASL_MOVE(value()));
+ }
+ return un_cvref_t<result_of_t<F(T)>>{};
+ }
};
template<typename T>
diff --git a/asl/tests/option_tests.cpp b/asl/tests/option_tests.cpp
index 73998f8..ea20b98 100644
--- a/asl/tests/option_tests.cpp
+++ b/asl/tests/option_tests.cpp
@@ -155,18 +155,18 @@ ASL_TEST(convert_copy)
asl::option<uint8_t> opt8 = uint8_t{8};
asl::option<uint16_t> opt16 = opt8;
- ASL_TEST_EXPECT(opt16.has_value());
+ ASL_TEST_ASSERT(opt16.has_value());
ASL_TEST_EXPECT(opt16.value() == 8);
opt8 = uint8_t{10};
- ASL_TEST_EXPECT(opt8.has_value());
+ ASL_TEST_ASSERT(opt8.has_value());
ASL_TEST_EXPECT(opt8.value() == 10);
opt16 = asl::nullopt;
ASL_TEST_EXPECT(!opt16.has_value());
opt16 = opt8;
- ASL_TEST_EXPECT(opt16.has_value());
+ ASL_TEST_ASSERT(opt16.has_value());
ASL_TEST_EXPECT(opt16.value() == 10);
}
@@ -175,18 +175,18 @@ ASL_TEST(convert_move)
asl::option<uint8_t> opt8 = uint8_t{8};
asl::option<uint16_t> opt16 = ASL_MOVE(opt8);
- ASL_TEST_EXPECT(opt16.has_value());
+ ASL_TEST_ASSERT(opt16.has_value());
ASL_TEST_EXPECT(opt16.value() == 8);
opt8 = ASL_MOVE(uint8_t{10});
- ASL_TEST_EXPECT(opt8.has_value());
+ ASL_TEST_ASSERT(opt8.has_value());
ASL_TEST_EXPECT(opt8.value() == 10);
opt16 = asl::nullopt;
ASL_TEST_EXPECT(!opt16.has_value());
opt16 = ASL_MOVE(opt8);
- ASL_TEST_EXPECT(opt16.has_value());
+ ASL_TEST_ASSERT(opt16.has_value());
ASL_TEST_EXPECT(opt16.value() == 10);
}
@@ -204,7 +204,7 @@ ASL_TEST(emplace)
asl::option<int> a = asl::nullopt;
a.emplace(42);
- ASL_TEST_EXPECT(a.has_value());
+ ASL_TEST_ASSERT(a.has_value());
ASL_TEST_EXPECT(a.value() == 42);
}
@@ -224,3 +224,20 @@ ASL_TEST(emplace_destroys_previous)
ASL_TEST_EXPECT(b2);
}
+
+ASL_TEST(and_then)
+{
+ asl::option<int> a = 5;
+ asl::option<int> b;
+
+ auto fn = [](int x) -> asl::option<float> { return static_cast<float>(x) + 0.5F; };
+
+ auto a2 = a.and_then(fn);
+ static_assert(asl::is_same<decltype(a2), asl::option<float>>);
+ ASL_TEST_ASSERT(a2.has_value());
+ ASL_TEST_EXPECT(a2.value() == 5.5F);
+
+ auto b2 = b.and_then(fn);
+ static_assert(asl::is_same<decltype(b2), asl::option<float>>);
+ ASL_TEST_ASSERT(!b2.has_value());
+}