diff options
author | 2022-09-28 10:38:46 -0400 | |
---|---|---|
committer | 2023-03-07 13:14:02 -0500 | |
commit | 07a71cdbe9c2a790e53c3c73dd0156a9c91727d2 (patch) | |
tree | 56b9615b6ae3fd80a4f6d45f41849f2e197fe96c | |
parent | 85795340f3b9deb44eb03e4aa498e7e14ae1522e (diff) |
FTL: Add Optional<T>::or_else
Bug: 185536303
Test: ftl_test
Change-Id: Idf0bac4711111c8cb28e8624bb146a76ec1ff38b
-rw-r--r-- | include/ftl/details/optional.h | 10 | ||||
-rw-r--r-- | include/ftl/optional.h | 14 | ||||
-rw-r--r-- | libs/ftl/optional_test.cpp | 40 |
3 files changed, 63 insertions, 1 deletions
diff --git a/include/ftl/details/optional.h b/include/ftl/details/optional.h index bff7c1e000..e45c1f5e23 100644 --- a/include/ftl/details/optional.h +++ b/include/ftl/details/optional.h @@ -54,5 +54,15 @@ struct and_then_result { template <typename F, typename T> using and_then_result_t = typename and_then_result<F, T>::type; +template <typename F, typename T> +struct or_else_result { + using type = remove_cvref_t<std::invoke_result_t<F>>; + static_assert(std::is_same_v<type, std::optional<T>> || std::is_same_v<type, Optional<T>>, + "or_else function must return an optional T"); +}; + +template <typename F, typename T> +using or_else_result_t = typename or_else_result<F, T>::type; + } // namespace details } // namespace android::ftl diff --git a/include/ftl/optional.h b/include/ftl/optional.h index a818128c92..94d8e3d7cb 100644 --- a/include/ftl/optional.h +++ b/include/ftl/optional.h @@ -96,13 +96,25 @@ struct Optional final : std::optional<T> { return R(); } + // Returns this Optional<T> if not nullopt, or else the Optional<T> returned by the function F. + template <typename F> + constexpr auto or_else(F&& f) const& -> details::or_else_result_t<F, T> { + if (has_value()) return *this; + return std::forward<F>(f)(); + } + + template <typename F> + constexpr auto or_else(F&& f) && -> details::or_else_result_t<F, T> { + if (has_value()) return std::move(*this); + return std::forward<F>(f)(); + } + // Delete new for this class. Its base doesn't have a virtual destructor, and // if it got deleted via base class pointer, it would cause undefined // behavior. There's not a good reason to allocate this object on the heap // anyway. static void* operator new(size_t) = delete; static void* operator new[](size_t) = delete; - }; template <typename T, typename U> diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp index 6b3b6c49e5..91bf7bc5b6 100644 --- a/libs/ftl/optional_test.cpp +++ b/libs/ftl/optional_test.cpp @@ -164,6 +164,46 @@ TEST(Optional, AndThen) { })); } +TEST(Optional, OrElse) { + // Non-empty. + { + const Optional opt = false; + EXPECT_EQ(false, opt.or_else([] { return Optional(true); })); + EXPECT_EQ('x', Optional('x').or_else([] { return std::make_optional('y'); })); + } + + // Empty. + { + const Optional<int> opt; + EXPECT_EQ(123, opt.or_else([]() -> Optional<int> { return 123; })); + EXPECT_EQ("abc"s, Optional<std::string>().or_else([] { return Optional("abc"s); })); + } + { + bool empty = false; + EXPECT_EQ(Optional<float>(), Optional<float>().or_else([&empty]() -> Optional<float> { + empty = true; + return std::nullopt; + })); + EXPECT_TRUE(empty); + } + + // Chaining. + using StringVector = StaticVector<std::string, 3>; + EXPECT_EQ(999, Optional(StaticVector{"1"s, "0"s, "0"s}) + .and_then([](StringVector&& v) -> Optional<StringVector> { + if (v.push_back("0"s)) return v; + return {}; + }) + .or_else([] { + return Optional(StaticVector{"9"s, "9"s, "9"s}); + }) + .transform([](const StringVector& v) { + return std::accumulate(v.begin(), v.end(), std::string()); + }) + .and_then(parse_int) + .or_else([] { return Optional(-1); })); +} + // Comparison. namespace { |