From 6d8bd01c3e73c15ee59f489118df67284dcb03e3 Mon Sep 17 00:00:00 2001 From: Lucian Grijincu Date: Sun, 28 Mar 2021 11:56:13 -0700 Subject: [PATCH] folly::Try: add value_or() implementations mimicking std::optional::value_or API Summary: Add API equivalent to `std::optional::value_or`: https://en.cppreference.com/w/cpp/utility/optional/value_or Returns the contained value if *this has a value, otherwise returns default_value. template< class U > constexpr T value_or( U&& default_value ) const&; Equivalent to `bool(*this) ? **this : static_cast(std::forward(default_value))` template< class U > constexpr T value_or( U&& default_value ) &&; Equivalent to `bool(*this) ? std::move(**this) : static_cast(std::forward(default_value))` Reviewed By: yfeldblum Differential Revision: D27346319 fbshipit-source-id: 598b740cb9068cffb0cc0fbda3579e64fd125e8b --- folly/Try-inl.h | 13 ++++++++++++ folly/Try.h | 11 ++++++++++ folly/test/TryTest.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/folly/Try-inl.h b/folly/Try-inl.h index af6d0b84b3e..ee7b2811d63 100644 --- a/folly/Try-inl.h +++ b/folly/Try-inl.h @@ -168,6 +168,19 @@ const T&& Try::value() const&& { return std::move(value_); } +template +template +T Try::value_or(U&& defaultValue) const& { + return hasValue() ? **this : static_cast(static_cast(defaultValue)); +} + +template +template +T Try::value_or(U&& defaultValue) && { + return hasValue() ? std::move(**this) + : static_cast(static_cast(defaultValue)); +} + template void Try::throwUnlessValue() const { switch (contains_) { diff --git a/folly/Try.h b/folly/Try.h index 5f79f9128e6..dd053646e5e 100644 --- a/folly/Try.h +++ b/folly/Try.h @@ -178,6 +178,17 @@ class Try { */ const T&& value() const&&; + /* + * Returns a copy of the contained value if *this has a value, + * otherwise returns a value constructed from defaultValue. + * + * The selected constructor of the return value may throw exceptions. + */ + template + T value_or(U&& defaultValue) const&; + template + T value_or(U&& defaultValue) &&; + /* * [Re]throw if the Try contains an exception or is empty. Otherwise do * nothing. diff --git a/folly/test/TryTest.cpp b/folly/test/TryTest.cpp index 062d5c8fb4f..ad0025a705f 100644 --- a/folly/test/TryTest.cpp +++ b/folly/test/TryTest.cpp @@ -192,6 +192,8 @@ TEST(Try, emplaceWithThrowingConstructor) { EXPECT_FALSE(t.hasValue()); EXPECT_FALSE(t.hasException()); + + EXPECT_THROW(t.value_or(true), MyException); } { @@ -202,6 +204,8 @@ TEST(Try, emplaceWithThrowingConstructor) { EXPECT_THROW(t.emplace(true), MyException); EXPECT_FALSE(t.hasValue()); EXPECT_FALSE(t.hasException()); + + EXPECT_THROW(t.value_or(true), MyException); } } @@ -376,6 +380,7 @@ TEST(Try, MoveDereference) { auto t = Try>{std::move(ptr)}; auto result = *std::move(t); EXPECT_EQ(*result, 1); + EXPECT_TRUE(t.hasValue()); } TEST(Try, MoveConstRvalue) { @@ -429,6 +434,47 @@ TEST(Try, ValueOverloads) { } } +TEST(Try, ValueOr) { + struct CopyableValue { + int x; + }; + { + Try o{CopyableValue{42}}; + CopyableValue defaultValue{17}; + EXPECT_EQ(o.value_or(defaultValue).x, 42); + EXPECT_EQ(o.value_or(defaultValue).x, (*o).x); + } + + { + Try empty; + EXPECT_FALSE(empty.hasValue()); + CopyableValue defaultValue{17}; + EXPECT_EQ(empty.value_or(defaultValue).x, defaultValue.x); + } + + { + Try> o{std::make_unique(42)}; + std::unique_ptr defaultValue = std::make_unique(17); + std::unique_ptr v = std::move(o).value_or(std::move(defaultValue)); + ASSERT_TRUE(v); + EXPECT_EQ(*v, 42); + ASSERT_TRUE(defaultValue); + EXPECT_EQ(*defaultValue, 17); + EXPECT_TRUE(o.hasValue()); + ASSERT_FALSE(*o); + } + + { + Try> empty; + std::unique_ptr defaultValue = std::make_unique(17); + std::unique_ptr v = std::move(empty).value_or(std::move(defaultValue)); + ASSERT_TRUE(v); + EXPECT_EQ(*v, 17); + EXPECT_FALSE(defaultValue); + EXPECT_FALSE(empty.hasValue()); + } +} + // Make sure we can copy Trys for copyable types TEST(Try, copy) { Try t;