diff --git a/base/expected_test.cpp b/base/expected_test.cpp index 490ced456..a74bc1dec 100644 --- a/base/expected_test.cpp +++ b/base/expected_test.cpp @@ -29,6 +29,7 @@ typedef expected exp_int; typedef expected exp_double; typedef expected exp_string; typedef expected, int> exp_pair; +typedef expected exp_void; struct T { int a; @@ -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) { @@ -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) { @@ -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) { @@ -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()); @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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 e = 10; + expected e2 = 10; + EXPECT_TRUE(e == e2); + e2 = 20; + EXPECT_FALSE(e == e2); + + expected e3 = "hello"; + expected e4 = "hello"; + EXPECT_TRUE(e3 == e4); + e4 = "world"; + EXPECT_FALSE(e3 == e4); + + expected e5; + expected e6 = 10; + EXPECT_FALSE(e5 == e6); + EXPECT_FALSE(e6 == e5); } TEST(Expected, testDivideExample) { @@ -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; diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h index 23072171a..08c9fb50d 100644 --- a/base/include/android-base/expected.h +++ b/base/include/android-base/expected.h @@ -412,13 +412,7 @@ constexpr bool operator==(const expected& x, const expected& y) template constexpr bool operator!=(const expected& x, const expected& 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 @@ -457,6 +451,194 @@ constexpr bool operator!=(const unexpected& x, const expected& y) { return y.has_value() || (x.value() != y.error()); } +template +class _NODISCARD_ expected { + public: + using value_type = void; + using error_type = E; + using unexpected_type = unexpected; + + // constructors + constexpr expected() = default; + constexpr expected(const expected& rhs) = default; + constexpr expected(expected&& rhs) noexcept = default; + + template && + std::is_convertible_v /* non-explicit */ + )> + constexpr expected(const expected& rhs) { + if (!rhs.has_value()) var_ = unexpected(rhs.error()); + } + + template && + !std::is_convertible_v /* explicit */ + )> + constexpr explicit expected(const expected& rhs) { + if (!rhs.has_value()) var_ = unexpected(rhs.error()); + } + + template && + std::is_convertible_v /* non-explicit */ + )> + constexpr expected(expected&& rhs) { + if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error())); + } + + template && + !std::is_convertible_v /* explicit */ + )> + constexpr explicit expected(expected&& rhs) { + if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error())); + } + + template && + std::is_convertible_v /* non-explicit */ + )> + constexpr expected(const unexpected& e) + : var_(std::in_place_index<1>, e.value()) {} + + template && + !std::is_convertible_v /* explicit */ + )> + constexpr explicit expected(const unexpected& e) + : var_(std::in_place_index<1>, E(e.value())) {} + + template && + std::is_convertible_v /* non-explicit */ + )> + constexpr expected(unexpected&& e) + : var_(std::in_place_index<1>, std::move(e.value())) {} + + template && + !std::is_convertible_v /* explicit */ + )> + constexpr explicit expected(unexpected&& e) + : var_(std::in_place_index<1>, E(std::move(e.value()))) {} + + template + constexpr explicit expected(std::in_place_t, Args&&...) {} + + template + )> + constexpr explicit expected(unexpect_t, Args&&... args) + : var_(unexpected_type(std::forward(args)...)) {} + + template&, Args...> + )> + constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) + : var_(unexpected_type(il, std::forward(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 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 + expected& operator=(const unexpected& rhs) { + var_ = rhs; + return *this; + } + + template && + std::is_move_assignable_v + )> + expected& operator=(unexpected&& rhs) { + var_ = std::move(rhs); + return *this; + } + + // modifiers + void emplace() { + var_ = std::monostate(); + } + + // swap + template> + > + void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v) { + 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(var_).value(); } + constexpr E& error() & { return std::get(var_).value(); } + constexpr const E&& error() const&& { return std::move(std::get(var_)).value(); } + constexpr E&& error() && { return std::move(std::get(var_)).value(); } + + // expected equality operators + template + friend constexpr bool operator==(const expected& x, const expected& y); + + // Specialized algorithms + template + friend void swap(expected&, expected&) noexcept; + + private: + std::variant var_; +}; + +template +constexpr bool operator==(const expected& x, const expected& y) { + if (x.has_value() != y.has_value()) { + return false; + } else if (!x.has_value()) { + return x.error() == y.error(); + } else { + return true; + } +} + +template +constexpr bool operator==(const expected& x, const expected& y) { + if (x.has_value() != y.has_value()) { + return false; + } else if (!x.has_value()) { + return x.error() == y.error(); + } else { + return false; + } +} + +template +constexpr bool operator==(const expected& x, const expected& 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 unexpected { public: diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h index 3cb39e6e2..8f0071051 100644 --- a/base/include/android-base/result.h +++ b/base/include/android-base/result.h @@ -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; diff --git a/base/result_test.cpp b/base/result_test.cpp index 687488991..d31e775e5 100644 --- a/base/result_test.cpp +++ b/base/result_test.cpp @@ -70,6 +70,31 @@ TEST(result, result_success_rvalue) { EXPECT_EQ(Success(), MakeRvalueSuccessResult().value()); } +TEST(result, result_void) { + Result ok = {}; + EXPECT_TRUE(ok); + ok.value(); // should not crash + ASSERT_DEATH(ok.error(), ""); + + Result 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 { + 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 result = Error() << "failure" << 1; ASSERT_FALSE(result);