diff options
Diffstat (limited to 'absl/status')
-rw-r--r-- | absl/status/BUILD.bazel | 2 | ||||
-rw-r--r-- | absl/status/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/status/statusor.cc | 36 | ||||
-rw-r--r-- | absl/status/statusor.h | 28 | ||||
-rw-r--r-- | absl/status/statusor_test.cc | 70 |
5 files changed, 109 insertions, 28 deletions
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 189bd73d..30df22ae 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -78,6 +78,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":status", + "//absl/base", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/meta:type_traits", @@ -96,6 +97,7 @@ cc_test( ":statusor", "//absl/base", "//absl/memory", + "//absl/strings", "//absl/types:any", "//absl/utility", "@com_google_googletest//:gtest_main", diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index 1248dff0..43898564 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt @@ -64,6 +64,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base absl::status absl::core_headers absl::raw_logging_internal diff --git a/absl/status/statusor.cc b/absl/status/statusor.cc index b954b45e..96642b34 100644 --- a/absl/status/statusor.cc +++ b/absl/status/statusor.cc @@ -16,6 +16,7 @@ #include <cstdlib> #include <utility> +#include "absl/base/call_once.h" #include "absl/base/internal/raw_logging.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" @@ -26,13 +27,44 @@ ABSL_NAMESPACE_BEGIN BadStatusOrAccess::BadStatusOrAccess(absl::Status status) : status_(std::move(status)) {} -BadStatusOrAccess::~BadStatusOrAccess() = default; +BadStatusOrAccess::BadStatusOrAccess(const BadStatusOrAccess& other) + : status_(other.status_) {} + +BadStatusOrAccess& BadStatusOrAccess::operator=( + const BadStatusOrAccess& other) { + // Ensure assignment is correct regardless of whether this->InitWhat() has + // already been called. + other.InitWhat(); + status_ = other.status_; + what_ = other.what_; + return *this; +} + +BadStatusOrAccess& BadStatusOrAccess::operator=(BadStatusOrAccess&& other) { + // Ensure assignment is correct regardless of whether this->InitWhat() has + // already been called. + other.InitWhat(); + status_ = std::move(other.status_); + what_ = std::move(other.what_); + return *this; +} + +BadStatusOrAccess::BadStatusOrAccess(BadStatusOrAccess&& other) + : status_(std::move(other.status_)) {} + const char* BadStatusOrAccess::what() const noexcept { - return "Bad StatusOr access"; + InitWhat(); + return what_.c_str(); } const absl::Status& BadStatusOrAccess::status() const { return status_; } +void BadStatusOrAccess::InitWhat() const { + absl::call_once(init_what_, [this] { + what_ = absl::StrCat("Bad StatusOr access: ", status_.ToString()); + }); +} + namespace internal_statusor { void Helper::HandleInvalidStatusCtorArg(absl::Status* status) { diff --git a/absl/status/statusor.h b/absl/status/statusor.h index b7c55cc8..235a3433 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -44,6 +44,7 @@ #include <utility> #include "absl/base/attributes.h" +#include "absl/base/call_once.h" #include "absl/meta/type_traits.h" #include "absl/status/internal/statusor_internal.h" #include "absl/status/status.h" @@ -72,13 +73,18 @@ ABSL_NAMESPACE_BEGIN class BadStatusOrAccess : public std::exception { public: explicit BadStatusOrAccess(absl::Status status); - ~BadStatusOrAccess() override; + ~BadStatusOrAccess() override = default; + + BadStatusOrAccess(const BadStatusOrAccess& other); + BadStatusOrAccess& operator=(const BadStatusOrAccess& other); + BadStatusOrAccess(BadStatusOrAccess&& other); + BadStatusOrAccess& operator=(BadStatusOrAccess&& other); // BadStatusOrAccess::what() // // Returns the associated explanatory string of the `absl::StatusOr<T>` - // object's error code. This function only returns the string literal "Bad - // StatusOr Access" for cases when evaluating general exceptions. + // object's error code. This function contains information about the failing + // status, but its exact formatting may change and should not be depended on. // // The pointer of this string is guaranteed to be valid until any non-const // function is invoked on the exception object. @@ -91,7 +97,11 @@ class BadStatusOrAccess : public std::exception { const absl::Status& status() const; private: + void InitWhat() const; + absl::Status status_; + mutable absl::once_flag init_what_; + mutable std::string what_; }; // Returned StatusOr objects may not be ignored. @@ -437,8 +447,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, T, U&&>>>>>::value, int> = 0> StatusOr(U&& u) // NOLINT - : StatusOr(absl::in_place, std::forward<U>(u)) { - } + : StatusOr(absl::in_place, std::forward<U>(u)) {} template < typename U = T, @@ -457,8 +466,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, absl::negation<std::is_convertible<U&&, T>>>::value, int> = 0> explicit StatusOr(U&& u) // NOLINT - : StatusOr(absl::in_place, std::forward<U>(u)) { - } + : StatusOr(absl::in_place, std::forward<U>(u)) {} // StatusOr<T>::ok() // @@ -481,7 +489,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, // Returns a reference to the current `absl::Status` contained within the // `absl::StatusOr<T>`. If `absl::StatusOr<T>` contains a `T`, then this // function returns `absl::OkStatus()`. - const Status& status() const &; + const Status& status() const&; Status status() &&; // StatusOr<T>::value() @@ -661,7 +669,9 @@ StatusOr<T>::StatusOr(absl::in_place_t, std::initializer_list<U> ilist, : Base(absl::in_place, ilist, std::forward<Args>(args)...) {} template <typename T> -const Status& StatusOr<T>::status() const & { return this->status_; } +const Status& StatusOr<T>::status() const& { + return this->status_; +} template <typename T> Status StatusOr<T>::status() && { return ok() ? OkStatus() : std::move(this->status_); diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc index c2e8fb7e..7cae90e1 100644 --- a/absl/status/statusor_test.cc +++ b/absl/status/statusor_test.cc @@ -17,6 +17,7 @@ #include <array> #include <initializer_list> #include <memory> +#include <string> #include <type_traits> #include <utility> @@ -25,6 +26,7 @@ #include "absl/base/casts.h" #include "absl/memory/memory.h" #include "absl/status/status.h" +#include "absl/strings/string_view.h" #include "absl/types/any.h" #include "absl/utility/utility.h" @@ -34,6 +36,7 @@ using ::testing::AllOf; using ::testing::AnyWith; using ::testing::ElementsAre; using ::testing::Field; +using ::testing::HasSubstr; using ::testing::Ne; using ::testing::Not; using ::testing::Pointee; @@ -257,9 +260,9 @@ TEST(StatusOr, TestMoveOnlyInitializationFromTemporaryByValueOrDie) { TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) { static_assert( - std::is_same<const int&&, - decltype( - std::declval<const absl::StatusOr<int>&&>().value())>(), + std::is_same< + const int&&, + decltype(std::declval<const absl::StatusOr<int>&&>().value())>(), "value() for const temporaries should return const T&&"); } @@ -303,20 +306,57 @@ TEST(StatusOr, StatusCtorForwards) { EXPECT_NE(status.message(), "Some error"); } +TEST(BadStatusOrAccessTest, CopyConstructionWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{e1}; + EXPECT_THAT(e1.what(), HasSubstr(error.ToString())); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, CopyAssignmentWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{absl::InternalError("other")}; + e2 = e1; + EXPECT_THAT(e1.what(), HasSubstr(error.ToString())); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, MoveConstructionWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{std::move(e1)}; + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, MoveAssignmentWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{absl::InternalError("other")}; + e2 = std::move(e1); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + // Define `EXPECT_DEATH_OR_THROW` to test the behavior of `StatusOr::value`, // which either throws `BadStatusOrAccess` or `LOG(FATAL)` based on whether // exceptions are enabled. #ifdef ABSL_HAVE_EXCEPTIONS -#define EXPECT_DEATH_OR_THROW(statement, status_) \ - EXPECT_THROW( \ - { \ - try { \ - statement; \ - } catch (const absl::BadStatusOrAccess& e) { \ - EXPECT_EQ(e.status(), status_); \ - throw; \ - } \ - }, \ +#define EXPECT_DEATH_OR_THROW(statement, status_) \ + EXPECT_THROW( \ + { \ + try { \ + statement; \ + } catch (const absl::BadStatusOrAccess& e) { \ + EXPECT_EQ(e.status(), status_); \ + EXPECT_THAT(e.what(), HasSubstr(e.status().ToString())); \ + throw; \ + } \ + }, \ absl::BadStatusOrAccess); #else // ABSL_HAVE_EXCEPTIONS #define EXPECT_DEATH_OR_THROW(statement, status) \ @@ -412,8 +452,6 @@ TEST(StatusOr, TestStatusCtor) { EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); } - - TEST(StatusOr, TestValueCtor) { const int kI = 4; const absl::StatusOr<int> thing(kI); @@ -1300,8 +1338,6 @@ TEST(StatusOr, TestPointerDefaultCtor) { EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); } - - TEST(StatusOr, TestPointerStatusCtor) { absl::StatusOr<int*> thing(absl::CancelledError()); EXPECT_FALSE(thing.ok()); |