Loading include/ftl/details/optional.h +10 −0 Original line number Diff line number Diff line Loading @@ -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 include/ftl/optional.h +13 −1 Original line number Diff line number Diff line Loading @@ -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> Loading libs/ftl/optional_test.cpp +40 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading
include/ftl/details/optional.h +10 −0 Original line number Diff line number Diff line Loading @@ -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
include/ftl/optional.h +13 −1 Original line number Diff line number Diff line Loading @@ -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> Loading
libs/ftl/optional_test.cpp +40 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading