Loading base/expected_test.cpp +149 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ typedef expected<int, int> exp_int; typedef expected<double, double> exp_double; typedef expected<std::string, std::string> exp_string; typedef expected<std::pair<std::string, int>, int> exp_pair; typedef expected<void, int> exp_void; struct T { int a; Loading Loading @@ -59,6 +60,9 @@ TEST(Expected, testDefaultConstructible) { exp_complex e2; EXPECT_TRUE(e2.has_value()); EXPECT_EQ(T(0,0), e2.value()); exp_void e3; EXPECT_TRUE(e3.has_value()); } TEST(Expected, testCopyConstructible) { Loading @@ -69,6 +73,11 @@ TEST(Expected, testCopyConstructible) { EXPECT_TRUE(e2.has_value()); EXPECT_EQ(0, e.value()); EXPECT_EQ(0, e2.value()); exp_void e3; exp_void e4 = e3; EXPECT_TRUE(e3.has_value()); EXPECT_TRUE(e4.has_value()); } TEST(Expected, testMoveConstructible) { Loading @@ -87,6 +96,11 @@ TEST(Expected, testMoveConstructible) { EXPECT_TRUE(e4.has_value()); EXPECT_EQ("", e3.value()); // e3 is moved EXPECT_EQ("hello", e4.value()); exp_void e5; exp_void e6 = std::move(e5); EXPECT_TRUE(e5.has_value()); EXPECT_TRUE(e6.has_value()); } TEST(Expected, testCopyConstructibleFromConvertibleType) { Loading Loading @@ -114,11 +128,13 @@ TEST(Expected, testConstructibleFromValue) { exp_double e2 = 5.5f; exp_string e3 = std::string("hello"); exp_complex e4 = T(10, 20); exp_void e5 = {}; EXPECT_TRUE(e.has_value()); EXPECT_TRUE(e2.has_value()); EXPECT_TRUE(e3.has_value()); EXPECT_TRUE(e4.has_value()); EXPECT_TRUE(e5.has_value()); EXPECT_EQ(3, e.value()); EXPECT_EQ(5.5f, e2.value()); EXPECT_EQ("hello", e3.value()); Loading Loading @@ -154,25 +170,33 @@ TEST(Expected, testConstructibleFromUnexpected) { exp_string::unexpected_type unexp3 = unexpected(std::string("error")); exp_string e3 = unexp3; exp_void::unexpected_type unexp4 = unexpected(10); exp_void e4 = unexp4; EXPECT_FALSE(e.has_value()); EXPECT_FALSE(e2.has_value()); EXPECT_FALSE(e3.has_value()); EXPECT_FALSE(e4.has_value()); EXPECT_EQ(10, e.error()); EXPECT_EQ(10.5f, e2.error()); EXPECT_EQ("error", e3.error()); EXPECT_EQ(10, e4.error()); } TEST(Expected, testMoveConstructibleFromUnexpected) { exp_int e = unexpected(10); exp_double e2 = unexpected(10.5f); exp_string e3 = unexpected(std::string("error")); exp_void e4 = unexpected(10); EXPECT_FALSE(e.has_value()); EXPECT_FALSE(e2.has_value()); EXPECT_FALSE(e3.has_value()); EXPECT_FALSE(e4.has_value()); EXPECT_EQ(10, e.error()); EXPECT_EQ(10.5f, e2.error()); EXPECT_EQ("error", e3.error()); EXPECT_EQ(10, e4.error()); } TEST(Expected, testConstructibleByForwarding) { Loading @@ -188,6 +212,9 @@ TEST(Expected, testConstructibleByForwarding) { EXPECT_TRUE(e3.has_value()); EXPECT_EQ("hello",e3->first); EXPECT_EQ(30,e3->second); exp_void e4({}); EXPECT_TRUE(e4.has_value()); } TEST(Expected, testDestructible) { Loading Loading @@ -217,6 +244,14 @@ TEST(Expected, testAssignable) { EXPECT_EQ(20, e3.value()); EXPECT_EQ(20, e4.value()); exp_void e5 = unexpected(10); ASSERT_FALSE(e5.has_value()); exp_void e6; e5 = e6; EXPECT_TRUE(e5.has_value()); EXPECT_TRUE(e6.has_value()); } TEST(Expected, testAssignableFromValue) { Loading @@ -231,6 +266,11 @@ TEST(Expected, testAssignableFromValue) { exp_string e3 = "hello"; e3 = "world"; EXPECT_EQ("world", e3.value()); exp_void e4 = unexpected(10); ASSERT_FALSE(e4.has_value()); e4 = {}; EXPECT_TRUE(e4.has_value()); } TEST(Expected, testAssignableFromUnexpected) { Loading @@ -248,6 +288,11 @@ TEST(Expected, testAssignableFromUnexpected) { e3 = unexpected("world"); EXPECT_FALSE(e3.has_value()); EXPECT_EQ("world", e3.error()); exp_void e4 = {}; e4 = unexpected(10); EXPECT_FALSE(e4.has_value()); EXPECT_EQ(10, e4.error()); } TEST(Expected, testAssignableFromMovedValue) { Loading Loading @@ -285,6 +330,11 @@ TEST(Expected, testEmplace) { EXPECT_EQ(10.5f, t.b); EXPECT_EQ(3, exp.value().a); EXPECT_EQ(10.5, exp.value().b); exp_void e = unexpected(10); ASSERT_FALSE(e.has_value()); e.emplace(); EXPECT_TRUE(e.has_value()); } TEST(Expected, testSwapExpectedExpected) { Loading @@ -296,6 +346,13 @@ TEST(Expected, testSwapExpectedExpected) { EXPECT_TRUE(e2.has_value()); EXPECT_EQ(20, e.value()); EXPECT_EQ(10, e2.value()); exp_void e3; exp_void e4; e3.swap(e4); EXPECT_TRUE(e3.has_value()); EXPECT_TRUE(e4.has_value()); } TEST(Expected, testSwapUnexpectedUnexpected) { Loading @@ -306,6 +363,14 @@ TEST(Expected, testSwapUnexpectedUnexpected) { EXPECT_FALSE(e2.has_value()); EXPECT_EQ(20, e.error()); EXPECT_EQ(10, e2.error()); exp_void e3 = unexpected(10); exp_void e4 = unexpected(20); e3.swap(e4); EXPECT_FALSE(e3.has_value()); EXPECT_FALSE(e4.has_value()); EXPECT_EQ(20, e3.error()); EXPECT_EQ(10, e4.error()); } TEST(Expected, testSwapExpectedUnepected) { Loading @@ -316,6 +381,13 @@ TEST(Expected, testSwapExpectedUnepected) { EXPECT_TRUE(e2.has_value()); EXPECT_EQ(30, e.error()); EXPECT_EQ(10, e2.value()); exp_void e3; exp_void e4 = unexpected(10); e3.swap(e4); EXPECT_FALSE(e3.has_value()); EXPECT_TRUE(e4.has_value()); EXPECT_EQ(10, e3.error()); } TEST(Expected, testDereference) { Loading Loading @@ -361,6 +433,13 @@ TEST(Expected, testSameValues) { EXPECT_TRUE(e2 == e); EXPECT_FALSE(e != e2); EXPECT_FALSE(e2 != e); exp_void e3; exp_void e4; EXPECT_TRUE(e3 == e4); EXPECT_TRUE(e4 == e3); EXPECT_FALSE(e3 != e4); EXPECT_FALSE(e4 != e3); } TEST(Expected, testDifferentValues) { Loading @@ -379,6 +458,13 @@ TEST(Expected, testValueWithError) { EXPECT_FALSE(e2 == e); EXPECT_TRUE(e != e2); EXPECT_TRUE(e2 != e); exp_void e3; exp_void e4 = unexpected(10); EXPECT_FALSE(e3 == e4); EXPECT_FALSE(e4 == e3); EXPECT_TRUE(e3 != e4); EXPECT_TRUE(e4 != e3); } TEST(Expected, testSameErrors) { Loading @@ -388,6 +474,13 @@ TEST(Expected, testSameErrors) { EXPECT_TRUE(e2 == e); EXPECT_FALSE(e != e2); EXPECT_FALSE(e2 != e); exp_void e3 = unexpected(10); exp_void e4 = unexpected(10); EXPECT_TRUE(e3 == e4); EXPECT_TRUE(e4 == e3); EXPECT_FALSE(e3 != e4); EXPECT_FALSE(e4 != e3); } TEST(Expected, testDifferentErrors) { Loading @@ -397,6 +490,13 @@ TEST(Expected, testDifferentErrors) { EXPECT_FALSE(e2 == e); EXPECT_TRUE(e != e2); EXPECT_TRUE(e2 != e); exp_void e3 = unexpected(10); exp_void e4 = unexpected(20); EXPECT_FALSE(e3 == e4); EXPECT_FALSE(e4 == e3); EXPECT_TRUE(e3 != e4); EXPECT_TRUE(e4 != e3); } TEST(Expected, testCompareWithSameValue) { Loading Loading @@ -424,6 +524,13 @@ TEST(Expected, testCompareWithSameError) { EXPECT_TRUE(error == e); EXPECT_FALSE(e != error); EXPECT_FALSE(error != e); exp_void e2 = unexpected(10); exp_void::unexpected_type error2 = 10; EXPECT_TRUE(e2 == error2); EXPECT_TRUE(error2 == e2); EXPECT_FALSE(e2 != error2); EXPECT_FALSE(error2 != e2); } TEST(Expected, testCompareWithDifferentError) { Loading @@ -433,6 +540,32 @@ TEST(Expected, testCompareWithDifferentError) { EXPECT_FALSE(error == e); EXPECT_TRUE(e != error); EXPECT_TRUE(error != e); exp_void e2 = unexpected(10); exp_void::unexpected_type error2 = 20; EXPECT_FALSE(e2 == error2); EXPECT_FALSE(error2 == e2); EXPECT_TRUE(e2 != error2); EXPECT_TRUE(error2 != e2); } TEST(Expected, testCompareDifferentType) { expected<int,int> e = 10; expected<int32_t, int> e2 = 10; EXPECT_TRUE(e == e2); e2 = 20; EXPECT_FALSE(e == e2); expected<std::string_view,int> e3 = "hello"; expected<std::string,int> e4 = "hello"; EXPECT_TRUE(e3 == e4); e4 = "world"; EXPECT_FALSE(e3 == e4); expected<void,int> e5; expected<int,int> e6 = 10; EXPECT_FALSE(e5 == e6); EXPECT_FALSE(e6 == e5); } TEST(Expected, testDivideExample) { Loading Loading @@ -478,6 +611,22 @@ TEST(Expected, testPair) { EXPECT_EQ("yes", r->first); } TEST(Expected, testVoid) { auto test = [](bool ok) -> exp_void { if (ok) { return {}; } else { return unexpected(10); } }; auto r = test(true); EXPECT_TRUE(r); r = test(false); EXPECT_FALSE(r); EXPECT_EQ(10, r.error()); } // copied from result_test.cpp struct ConstructorTracker { static size_t constructor_called; Loading base/include/android-base/expected.h +189 −7 Original line number Diff line number Diff line Loading @@ -412,13 +412,7 @@ constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) template<class T1, class E1, class T2, class E2> constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) { if (x.has_value() != y.has_value()) { return true; } else if (!x.has_value()) { return x.error() != y.error(); } else { return *x != *y; } return !(x == y); } // comparison with T Loading Loading @@ -457,6 +451,194 @@ constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) { return y.has_value() || (x.value() != y.error()); } template<class E> class _NODISCARD_ expected<void, E> { public: using value_type = void; using error_type = E; using unexpected_type = unexpected<E>; // constructors constexpr expected() = default; constexpr expected(const expected& rhs) = default; constexpr expected(expected&& rhs) noexcept = default; template<class U, class G _ENABLE_IF( std::is_void_v<U> && std::is_convertible_v<const G&, E> /* non-explicit */ )> constexpr expected(const expected<U, G>& rhs) { if (!rhs.has_value()) var_ = unexpected(rhs.error()); } template<class U, class G _ENABLE_IF( std::is_void_v<U> && !std::is_convertible_v<const G&, E> /* explicit */ )> constexpr explicit expected(const expected<U, G>& rhs) { if (!rhs.has_value()) var_ = unexpected(rhs.error()); } template<class U, class G _ENABLE_IF( std::is_void_v<U> && std::is_convertible_v<const G&&, E> /* non-explicit */ )> constexpr expected(expected<U, G>&& rhs) { if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error())); } template<class U, class G _ENABLE_IF( std::is_void_v<U> && !std::is_convertible_v<const G&&, E> /* explicit */ )> constexpr explicit expected(expected<U, G>&& rhs) { if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error())); } template<class G = E _ENABLE_IF( std::is_constructible_v<E, const G&> && std::is_convertible_v<const G&, E> /* non-explicit */ )> constexpr expected(const unexpected<G>& e) : var_(std::in_place_index<1>, e.value()) {} template<class G = E _ENABLE_IF( std::is_constructible_v<E, const G&> && !std::is_convertible_v<const G&, E> /* explicit */ )> constexpr explicit expected(const unexpected<G>& e) : var_(std::in_place_index<1>, E(e.value())) {} template<class G = E _ENABLE_IF( std::is_constructible_v<E, G&&> && std::is_convertible_v<G&&, E> /* non-explicit */ )> constexpr expected(unexpected<G>&& e) : var_(std::in_place_index<1>, std::move(e.value())) {} template<class G = E _ENABLE_IF( std::is_constructible_v<E, G&&> && !std::is_convertible_v<G&&, E> /* explicit */ )> constexpr explicit expected(unexpected<G>&& e) : var_(std::in_place_index<1>, E(std::move(e.value()))) {} template<class... Args _ENABLE_IF( sizeof...(Args) == 0 )> constexpr explicit expected(std::in_place_t, Args&&...) {} template<class... Args _ENABLE_IF( std::is_constructible_v<E, Args...> )> constexpr explicit expected(unexpect_t, Args&&... args) : var_(unexpected_type(std::forward<Args>(args)...)) {} template<class U, class... Args _ENABLE_IF( std::is_constructible_v<E, std::initializer_list<U>&, Args...> )> constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args) : var_(unexpected_type(il, std::forward<Args>(args)...)) {} // destructor ~expected() = default; // assignment // Note: SFNAIE doesn't work here because assignment operator should be // non-template. We could workaround this by defining a templated parent class // having the assignment operator. This incomplete implementation however // doesn't allow us to copy assign expected<T,E> even when T is non-copy // assignable. The copy assignment will fail by the underlying std::variant // anyway though the error message won't be clear. expected& operator=(const expected& rhs) = default; // Note for SFNAIE above applies to here as well expected& operator=(expected&& rhs) = default; template<class G = E> expected& operator=(const unexpected<G>& rhs) { var_ = rhs; return *this; } template<class G = E _ENABLE_IF( std::is_nothrow_move_constructible_v<G> && std::is_move_assignable_v<G> )> expected& operator=(unexpected<G>&& rhs) { var_ = std::move(rhs); return *this; } // modifiers void emplace() { var_ = std::monostate(); } // swap template<typename = std::enable_if_t< std::is_swappable_v<E>> > void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) { var_.swap(rhs.var_); } // observers constexpr explicit operator bool() const noexcept { return has_value(); } constexpr bool has_value() const noexcept { return var_.index() == 0; } constexpr void value() const& { if (!has_value()) std::get<0>(var_); } constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); } constexpr E& error() & { return std::get<unexpected_type>(var_).value(); } constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); } constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); } // expected equality operators template<class E1, class E2> friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y); // Specialized algorithms template<class T1, class E1> friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept; private: std::variant<std::monostate, unexpected_type> var_; }; template<class E1, class E2> constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) { if (x.has_value() != y.has_value()) { return false; } else if (!x.has_value()) { return x.error() == y.error(); } else { return true; } } template<class T1, class E1, class E2> constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) { if (x.has_value() != y.has_value()) { return false; } else if (!x.has_value()) { return x.error() == y.error(); } else { return false; } } template<class E1, class T2, class E2> constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) { if (x.has_value() != y.has_value()) { return false; } else if (!x.has_value()) { return x.error() == y.error(); } else { return false; } } template<class E> class unexpected { public: Loading base/include/android-base/result.h +8 −0 Original line number Diff line number Diff line Loading @@ -98,6 +98,14 @@ struct ResultError { int code_; }; inline bool operator==(const ResultError& lhs, const ResultError& rhs) { return lhs.message() == rhs.message() && lhs.code() == rhs.code(); } inline bool operator!=(const ResultError& lhs, const ResultError& rhs) { return !(lhs == rhs); } inline std::ostream& operator<<(std::ostream& os, const ResultError& t) { os << t.message(); return os; Loading base/result_test.cpp +25 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,31 @@ TEST(result, result_success_rvalue) { EXPECT_EQ(Success(), MakeRvalueSuccessResult().value()); } TEST(result, result_void) { Result<void> ok = {}; EXPECT_TRUE(ok); ok.value(); // should not crash ASSERT_DEATH(ok.error(), ""); Result<void> fail = Error() << "failure" << 1; EXPECT_FALSE(fail); EXPECT_EQ("failure1", fail.error().message()); EXPECT_EQ(0, fail.error().code()); EXPECT_TRUE(ok != fail); ASSERT_DEATH(fail.value(), ""); auto test = [](bool ok) -> Result<void> { if (ok) return {}; else return Error() << "failure" << 1; }; EXPECT_TRUE(test(true)); EXPECT_FALSE(test(false)); test(true).value(); // should not crash ASSERT_DEATH(test(true).error(), ""); ASSERT_DEATH(test(false).value(), ""); EXPECT_EQ("failure1", test(false).error().message()); } TEST(result, result_error) { Result<Success> result = Error() << "failure" << 1; ASSERT_FALSE(result); Loading Loading
base/expected_test.cpp +149 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ typedef expected<int, int> exp_int; typedef expected<double, double> exp_double; typedef expected<std::string, std::string> exp_string; typedef expected<std::pair<std::string, int>, int> exp_pair; typedef expected<void, int> exp_void; struct T { int a; Loading Loading @@ -59,6 +60,9 @@ TEST(Expected, testDefaultConstructible) { exp_complex e2; EXPECT_TRUE(e2.has_value()); EXPECT_EQ(T(0,0), e2.value()); exp_void e3; EXPECT_TRUE(e3.has_value()); } TEST(Expected, testCopyConstructible) { Loading @@ -69,6 +73,11 @@ TEST(Expected, testCopyConstructible) { EXPECT_TRUE(e2.has_value()); EXPECT_EQ(0, e.value()); EXPECT_EQ(0, e2.value()); exp_void e3; exp_void e4 = e3; EXPECT_TRUE(e3.has_value()); EXPECT_TRUE(e4.has_value()); } TEST(Expected, testMoveConstructible) { Loading @@ -87,6 +96,11 @@ TEST(Expected, testMoveConstructible) { EXPECT_TRUE(e4.has_value()); EXPECT_EQ("", e3.value()); // e3 is moved EXPECT_EQ("hello", e4.value()); exp_void e5; exp_void e6 = std::move(e5); EXPECT_TRUE(e5.has_value()); EXPECT_TRUE(e6.has_value()); } TEST(Expected, testCopyConstructibleFromConvertibleType) { Loading Loading @@ -114,11 +128,13 @@ TEST(Expected, testConstructibleFromValue) { exp_double e2 = 5.5f; exp_string e3 = std::string("hello"); exp_complex e4 = T(10, 20); exp_void e5 = {}; EXPECT_TRUE(e.has_value()); EXPECT_TRUE(e2.has_value()); EXPECT_TRUE(e3.has_value()); EXPECT_TRUE(e4.has_value()); EXPECT_TRUE(e5.has_value()); EXPECT_EQ(3, e.value()); EXPECT_EQ(5.5f, e2.value()); EXPECT_EQ("hello", e3.value()); Loading Loading @@ -154,25 +170,33 @@ TEST(Expected, testConstructibleFromUnexpected) { exp_string::unexpected_type unexp3 = unexpected(std::string("error")); exp_string e3 = unexp3; exp_void::unexpected_type unexp4 = unexpected(10); exp_void e4 = unexp4; EXPECT_FALSE(e.has_value()); EXPECT_FALSE(e2.has_value()); EXPECT_FALSE(e3.has_value()); EXPECT_FALSE(e4.has_value()); EXPECT_EQ(10, e.error()); EXPECT_EQ(10.5f, e2.error()); EXPECT_EQ("error", e3.error()); EXPECT_EQ(10, e4.error()); } TEST(Expected, testMoveConstructibleFromUnexpected) { exp_int e = unexpected(10); exp_double e2 = unexpected(10.5f); exp_string e3 = unexpected(std::string("error")); exp_void e4 = unexpected(10); EXPECT_FALSE(e.has_value()); EXPECT_FALSE(e2.has_value()); EXPECT_FALSE(e3.has_value()); EXPECT_FALSE(e4.has_value()); EXPECT_EQ(10, e.error()); EXPECT_EQ(10.5f, e2.error()); EXPECT_EQ("error", e3.error()); EXPECT_EQ(10, e4.error()); } TEST(Expected, testConstructibleByForwarding) { Loading @@ -188,6 +212,9 @@ TEST(Expected, testConstructibleByForwarding) { EXPECT_TRUE(e3.has_value()); EXPECT_EQ("hello",e3->first); EXPECT_EQ(30,e3->second); exp_void e4({}); EXPECT_TRUE(e4.has_value()); } TEST(Expected, testDestructible) { Loading Loading @@ -217,6 +244,14 @@ TEST(Expected, testAssignable) { EXPECT_EQ(20, e3.value()); EXPECT_EQ(20, e4.value()); exp_void e5 = unexpected(10); ASSERT_FALSE(e5.has_value()); exp_void e6; e5 = e6; EXPECT_TRUE(e5.has_value()); EXPECT_TRUE(e6.has_value()); } TEST(Expected, testAssignableFromValue) { Loading @@ -231,6 +266,11 @@ TEST(Expected, testAssignableFromValue) { exp_string e3 = "hello"; e3 = "world"; EXPECT_EQ("world", e3.value()); exp_void e4 = unexpected(10); ASSERT_FALSE(e4.has_value()); e4 = {}; EXPECT_TRUE(e4.has_value()); } TEST(Expected, testAssignableFromUnexpected) { Loading @@ -248,6 +288,11 @@ TEST(Expected, testAssignableFromUnexpected) { e3 = unexpected("world"); EXPECT_FALSE(e3.has_value()); EXPECT_EQ("world", e3.error()); exp_void e4 = {}; e4 = unexpected(10); EXPECT_FALSE(e4.has_value()); EXPECT_EQ(10, e4.error()); } TEST(Expected, testAssignableFromMovedValue) { Loading Loading @@ -285,6 +330,11 @@ TEST(Expected, testEmplace) { EXPECT_EQ(10.5f, t.b); EXPECT_EQ(3, exp.value().a); EXPECT_EQ(10.5, exp.value().b); exp_void e = unexpected(10); ASSERT_FALSE(e.has_value()); e.emplace(); EXPECT_TRUE(e.has_value()); } TEST(Expected, testSwapExpectedExpected) { Loading @@ -296,6 +346,13 @@ TEST(Expected, testSwapExpectedExpected) { EXPECT_TRUE(e2.has_value()); EXPECT_EQ(20, e.value()); EXPECT_EQ(10, e2.value()); exp_void e3; exp_void e4; e3.swap(e4); EXPECT_TRUE(e3.has_value()); EXPECT_TRUE(e4.has_value()); } TEST(Expected, testSwapUnexpectedUnexpected) { Loading @@ -306,6 +363,14 @@ TEST(Expected, testSwapUnexpectedUnexpected) { EXPECT_FALSE(e2.has_value()); EXPECT_EQ(20, e.error()); EXPECT_EQ(10, e2.error()); exp_void e3 = unexpected(10); exp_void e4 = unexpected(20); e3.swap(e4); EXPECT_FALSE(e3.has_value()); EXPECT_FALSE(e4.has_value()); EXPECT_EQ(20, e3.error()); EXPECT_EQ(10, e4.error()); } TEST(Expected, testSwapExpectedUnepected) { Loading @@ -316,6 +381,13 @@ TEST(Expected, testSwapExpectedUnepected) { EXPECT_TRUE(e2.has_value()); EXPECT_EQ(30, e.error()); EXPECT_EQ(10, e2.value()); exp_void e3; exp_void e4 = unexpected(10); e3.swap(e4); EXPECT_FALSE(e3.has_value()); EXPECT_TRUE(e4.has_value()); EXPECT_EQ(10, e3.error()); } TEST(Expected, testDereference) { Loading Loading @@ -361,6 +433,13 @@ TEST(Expected, testSameValues) { EXPECT_TRUE(e2 == e); EXPECT_FALSE(e != e2); EXPECT_FALSE(e2 != e); exp_void e3; exp_void e4; EXPECT_TRUE(e3 == e4); EXPECT_TRUE(e4 == e3); EXPECT_FALSE(e3 != e4); EXPECT_FALSE(e4 != e3); } TEST(Expected, testDifferentValues) { Loading @@ -379,6 +458,13 @@ TEST(Expected, testValueWithError) { EXPECT_FALSE(e2 == e); EXPECT_TRUE(e != e2); EXPECT_TRUE(e2 != e); exp_void e3; exp_void e4 = unexpected(10); EXPECT_FALSE(e3 == e4); EXPECT_FALSE(e4 == e3); EXPECT_TRUE(e3 != e4); EXPECT_TRUE(e4 != e3); } TEST(Expected, testSameErrors) { Loading @@ -388,6 +474,13 @@ TEST(Expected, testSameErrors) { EXPECT_TRUE(e2 == e); EXPECT_FALSE(e != e2); EXPECT_FALSE(e2 != e); exp_void e3 = unexpected(10); exp_void e4 = unexpected(10); EXPECT_TRUE(e3 == e4); EXPECT_TRUE(e4 == e3); EXPECT_FALSE(e3 != e4); EXPECT_FALSE(e4 != e3); } TEST(Expected, testDifferentErrors) { Loading @@ -397,6 +490,13 @@ TEST(Expected, testDifferentErrors) { EXPECT_FALSE(e2 == e); EXPECT_TRUE(e != e2); EXPECT_TRUE(e2 != e); exp_void e3 = unexpected(10); exp_void e4 = unexpected(20); EXPECT_FALSE(e3 == e4); EXPECT_FALSE(e4 == e3); EXPECT_TRUE(e3 != e4); EXPECT_TRUE(e4 != e3); } TEST(Expected, testCompareWithSameValue) { Loading Loading @@ -424,6 +524,13 @@ TEST(Expected, testCompareWithSameError) { EXPECT_TRUE(error == e); EXPECT_FALSE(e != error); EXPECT_FALSE(error != e); exp_void e2 = unexpected(10); exp_void::unexpected_type error2 = 10; EXPECT_TRUE(e2 == error2); EXPECT_TRUE(error2 == e2); EXPECT_FALSE(e2 != error2); EXPECT_FALSE(error2 != e2); } TEST(Expected, testCompareWithDifferentError) { Loading @@ -433,6 +540,32 @@ TEST(Expected, testCompareWithDifferentError) { EXPECT_FALSE(error == e); EXPECT_TRUE(e != error); EXPECT_TRUE(error != e); exp_void e2 = unexpected(10); exp_void::unexpected_type error2 = 20; EXPECT_FALSE(e2 == error2); EXPECT_FALSE(error2 == e2); EXPECT_TRUE(e2 != error2); EXPECT_TRUE(error2 != e2); } TEST(Expected, testCompareDifferentType) { expected<int,int> e = 10; expected<int32_t, int> e2 = 10; EXPECT_TRUE(e == e2); e2 = 20; EXPECT_FALSE(e == e2); expected<std::string_view,int> e3 = "hello"; expected<std::string,int> e4 = "hello"; EXPECT_TRUE(e3 == e4); e4 = "world"; EXPECT_FALSE(e3 == e4); expected<void,int> e5; expected<int,int> e6 = 10; EXPECT_FALSE(e5 == e6); EXPECT_FALSE(e6 == e5); } TEST(Expected, testDivideExample) { Loading Loading @@ -478,6 +611,22 @@ TEST(Expected, testPair) { EXPECT_EQ("yes", r->first); } TEST(Expected, testVoid) { auto test = [](bool ok) -> exp_void { if (ok) { return {}; } else { return unexpected(10); } }; auto r = test(true); EXPECT_TRUE(r); r = test(false); EXPECT_FALSE(r); EXPECT_EQ(10, r.error()); } // copied from result_test.cpp struct ConstructorTracker { static size_t constructor_called; Loading
base/include/android-base/expected.h +189 −7 Original line number Diff line number Diff line Loading @@ -412,13 +412,7 @@ constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) template<class T1, class E1, class T2, class E2> constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) { if (x.has_value() != y.has_value()) { return true; } else if (!x.has_value()) { return x.error() != y.error(); } else { return *x != *y; } return !(x == y); } // comparison with T Loading Loading @@ -457,6 +451,194 @@ constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) { return y.has_value() || (x.value() != y.error()); } template<class E> class _NODISCARD_ expected<void, E> { public: using value_type = void; using error_type = E; using unexpected_type = unexpected<E>; // constructors constexpr expected() = default; constexpr expected(const expected& rhs) = default; constexpr expected(expected&& rhs) noexcept = default; template<class U, class G _ENABLE_IF( std::is_void_v<U> && std::is_convertible_v<const G&, E> /* non-explicit */ )> constexpr expected(const expected<U, G>& rhs) { if (!rhs.has_value()) var_ = unexpected(rhs.error()); } template<class U, class G _ENABLE_IF( std::is_void_v<U> && !std::is_convertible_v<const G&, E> /* explicit */ )> constexpr explicit expected(const expected<U, G>& rhs) { if (!rhs.has_value()) var_ = unexpected(rhs.error()); } template<class U, class G _ENABLE_IF( std::is_void_v<U> && std::is_convertible_v<const G&&, E> /* non-explicit */ )> constexpr expected(expected<U, G>&& rhs) { if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error())); } template<class U, class G _ENABLE_IF( std::is_void_v<U> && !std::is_convertible_v<const G&&, E> /* explicit */ )> constexpr explicit expected(expected<U, G>&& rhs) { if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error())); } template<class G = E _ENABLE_IF( std::is_constructible_v<E, const G&> && std::is_convertible_v<const G&, E> /* non-explicit */ )> constexpr expected(const unexpected<G>& e) : var_(std::in_place_index<1>, e.value()) {} template<class G = E _ENABLE_IF( std::is_constructible_v<E, const G&> && !std::is_convertible_v<const G&, E> /* explicit */ )> constexpr explicit expected(const unexpected<G>& e) : var_(std::in_place_index<1>, E(e.value())) {} template<class G = E _ENABLE_IF( std::is_constructible_v<E, G&&> && std::is_convertible_v<G&&, E> /* non-explicit */ )> constexpr expected(unexpected<G>&& e) : var_(std::in_place_index<1>, std::move(e.value())) {} template<class G = E _ENABLE_IF( std::is_constructible_v<E, G&&> && !std::is_convertible_v<G&&, E> /* explicit */ )> constexpr explicit expected(unexpected<G>&& e) : var_(std::in_place_index<1>, E(std::move(e.value()))) {} template<class... Args _ENABLE_IF( sizeof...(Args) == 0 )> constexpr explicit expected(std::in_place_t, Args&&...) {} template<class... Args _ENABLE_IF( std::is_constructible_v<E, Args...> )> constexpr explicit expected(unexpect_t, Args&&... args) : var_(unexpected_type(std::forward<Args>(args)...)) {} template<class U, class... Args _ENABLE_IF( std::is_constructible_v<E, std::initializer_list<U>&, Args...> )> constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args) : var_(unexpected_type(il, std::forward<Args>(args)...)) {} // destructor ~expected() = default; // assignment // Note: SFNAIE doesn't work here because assignment operator should be // non-template. We could workaround this by defining a templated parent class // having the assignment operator. This incomplete implementation however // doesn't allow us to copy assign expected<T,E> even when T is non-copy // assignable. The copy assignment will fail by the underlying std::variant // anyway though the error message won't be clear. expected& operator=(const expected& rhs) = default; // Note for SFNAIE above applies to here as well expected& operator=(expected&& rhs) = default; template<class G = E> expected& operator=(const unexpected<G>& rhs) { var_ = rhs; return *this; } template<class G = E _ENABLE_IF( std::is_nothrow_move_constructible_v<G> && std::is_move_assignable_v<G> )> expected& operator=(unexpected<G>&& rhs) { var_ = std::move(rhs); return *this; } // modifiers void emplace() { var_ = std::monostate(); } // swap template<typename = std::enable_if_t< std::is_swappable_v<E>> > void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) { var_.swap(rhs.var_); } // observers constexpr explicit operator bool() const noexcept { return has_value(); } constexpr bool has_value() const noexcept { return var_.index() == 0; } constexpr void value() const& { if (!has_value()) std::get<0>(var_); } constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); } constexpr E& error() & { return std::get<unexpected_type>(var_).value(); } constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); } constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); } // expected equality operators template<class E1, class E2> friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y); // Specialized algorithms template<class T1, class E1> friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept; private: std::variant<std::monostate, unexpected_type> var_; }; template<class E1, class E2> constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) { if (x.has_value() != y.has_value()) { return false; } else if (!x.has_value()) { return x.error() == y.error(); } else { return true; } } template<class T1, class E1, class E2> constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) { if (x.has_value() != y.has_value()) { return false; } else if (!x.has_value()) { return x.error() == y.error(); } else { return false; } } template<class E1, class T2, class E2> constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) { if (x.has_value() != y.has_value()) { return false; } else if (!x.has_value()) { return x.error() == y.error(); } else { return false; } } template<class E> class unexpected { public: Loading
base/include/android-base/result.h +8 −0 Original line number Diff line number Diff line Loading @@ -98,6 +98,14 @@ struct ResultError { int code_; }; inline bool operator==(const ResultError& lhs, const ResultError& rhs) { return lhs.message() == rhs.message() && lhs.code() == rhs.code(); } inline bool operator!=(const ResultError& lhs, const ResultError& rhs) { return !(lhs == rhs); } inline std::ostream& operator<<(std::ostream& os, const ResultError& t) { os << t.message(); return os; Loading
base/result_test.cpp +25 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,31 @@ TEST(result, result_success_rvalue) { EXPECT_EQ(Success(), MakeRvalueSuccessResult().value()); } TEST(result, result_void) { Result<void> ok = {}; EXPECT_TRUE(ok); ok.value(); // should not crash ASSERT_DEATH(ok.error(), ""); Result<void> fail = Error() << "failure" << 1; EXPECT_FALSE(fail); EXPECT_EQ("failure1", fail.error().message()); EXPECT_EQ(0, fail.error().code()); EXPECT_TRUE(ok != fail); ASSERT_DEATH(fail.value(), ""); auto test = [](bool ok) -> Result<void> { if (ok) return {}; else return Error() << "failure" << 1; }; EXPECT_TRUE(test(true)); EXPECT_FALSE(test(false)); test(true).value(); // should not crash ASSERT_DEATH(test(true).error(), ""); ASSERT_DEATH(test(false).value(), ""); EXPECT_EQ("failure1", test(false).error().message()); } TEST(result, result_error) { Result<Success> result = Error() << "failure" << 1; ASSERT_FALSE(result); Loading