diff options
Diffstat (limited to 'absl/status')
-rw-r--r-- | absl/status/CMakeLists.txt | 2 | ||||
-rw-r--r-- | absl/status/internal/status_internal.h | 4 | ||||
-rw-r--r-- | absl/status/internal/statusor_internal.h | 36 | ||||
-rw-r--r-- | absl/status/status.cc | 18 | ||||
-rw-r--r-- | absl/status/status.h | 13 | ||||
-rw-r--r-- | absl/status/status_test.cc | 23 | ||||
-rw-r--r-- | absl/status/statusor.h | 30 | ||||
-rw-r--r-- | absl/status/statusor_test.cc | 33 |
8 files changed, 118 insertions, 41 deletions
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index 15db36af..4a3c5d68 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt @@ -25,6 +25,8 @@ absl_cc_library( "status_payload_printer.cc" COPTS ${ABSL_DEFAULT_COPTS} + DEFINES + "$<$<PLATFORM_ID:AIX>:_LINUX_SOURCE_COMPAT>" DEPS absl::atomic_hook absl::config diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index 873eb5c2..6198e726 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h @@ -66,6 +66,10 @@ struct StatusRep { std::atomic<int32_t> ref; absl::StatusCode code; + + // As an internal implementation detail, we guarantee that if status.message() + // is non-empty, then the resulting string_view is null terminated. + // This is required to implement 'StatusMessageAsCStr(...)' std::string message; std::unique_ptr<status_internal::Payloads> payloads; }; diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h index eaac2c0b..49cead7a 100644 --- a/absl/status/internal/statusor_internal.h +++ b/absl/status/internal/statusor_internal.h @@ -69,11 +69,8 @@ using IsConstructibleOrConvertibleOrAssignableFromStatusOr = template <typename T, typename U> struct IsDirectInitializationAmbiguous : public absl::conditional_t< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, - U>::value, - std::false_type, - IsDirectInitializationAmbiguous< - T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; + std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type, + IsDirectInitializationAmbiguous<T, absl::remove_cvref_t<U>>> {}; template <typename T, typename V> struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>> @@ -84,14 +81,11 @@ struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>> template <typename T, typename U> using IsDirectInitializationValid = absl::disjunction< // Short circuits if T is basically U. - std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<T, absl::remove_cvref_t<U>>, absl::negation<absl::disjunction< - std::is_same<absl::StatusOr<T>, - absl::remove_cv_t<absl::remove_reference_t<U>>>, - std::is_same<absl::Status, - absl::remove_cv_t<absl::remove_reference_t<U>>>, - std::is_same<absl::in_place_t, - absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>, + std::is_same<absl::Status, absl::remove_cvref_t<U>>, + std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>, IsDirectInitializationAmbiguous<T, U>>>>; // This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which @@ -107,11 +101,8 @@ using IsDirectInitializationValid = absl::disjunction< template <typename T, typename U> struct IsForwardingAssignmentAmbiguous : public absl::conditional_t< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, - U>::value, - std::false_type, - IsForwardingAssignmentAmbiguous< - T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; + std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type, + IsForwardingAssignmentAmbiguous<T, absl::remove_cvref_t<U>>> {}; template <typename T, typename U> struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>> @@ -122,14 +113,11 @@ struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>> template <typename T, typename U> using IsForwardingAssignmentValid = absl::disjunction< // Short circuits if T is basically U. - std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<T, absl::remove_cvref_t<U>>, absl::negation<absl::disjunction< - std::is_same<absl::StatusOr<T>, - absl::remove_cv_t<absl::remove_reference_t<U>>>, - std::is_same<absl::Status, - absl::remove_cv_t<absl::remove_reference_t<U>>>, - std::is_same<absl::in_place_t, - absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>, + std::is_same<absl::Status, absl::remove_cvref_t<U>>, + std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>, IsForwardingAssignmentAmbiguous<T, U>>>>; class Helper { diff --git a/absl/status/status.cc b/absl/status/status.cc index 160eb417..26e68294 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -80,10 +80,8 @@ std::ostream& operator<<(std::ostream& os, StatusCode code) { namespace status_internal { static absl::optional<size_t> FindPayloadIndexByUrl( - const Payloads* payloads, - absl::string_view type_url) { - if (payloads == nullptr) - return absl::nullopt; + const Payloads* payloads, absl::string_view type_url) { + if (payloads == nullptr) return absl::nullopt; for (size_t i = 0; i < payloads->size(); ++i) { if ((*payloads)[i].type_url == type_url) return i; @@ -125,8 +123,7 @@ absl::optional<absl::Cord> Status::GetPayload( const auto* payloads = GetPayloads(); absl::optional<size_t> index = status_internal::FindPayloadIndexByUrl(payloads, type_url); - if (index.has_value()) - return (*payloads)[index.value()].payload; + if (index.has_value()) return (*payloads)[index.value()].payload; return absl::nullopt; } @@ -303,7 +300,7 @@ std::string Status::ToStringSlow(StatusToStringMode mode) const { absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message()); const bool with_payload = (mode & StatusToStringMode::kWithPayload) == - StatusToStringMode::kWithPayload; + StatusToStringMode::kWithPayload; if (with_payload) { status_internal::StatusPayloadPrinter printer = @@ -619,5 +616,12 @@ std::string* MakeCheckFailString(const absl::Status* status, } // namespace status_internal +const char* StatusMessageAsCStr(const Status& status) { + // As an internal implementation detail, we guarantee that if status.message() + // is non-empty, then the resulting string_view is null terminated. + auto sv_message = status.message(); + return sv_message.empty() ? "" : sv_message.data(); +} + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/status/status.h b/absl/status/status.h index 4e8292fc..595064c0 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -398,7 +398,7 @@ inline StatusToStringMode& operator^=(StatusToStringMode& lhs, // // * It may provide more fine-grained semantic information about the error to // facilitate actionable remedies. -// * It may provide human-readable contexual information that is more +// * It may provide human-readable contextual information that is more // appropriate to display to an end user. // // Example: @@ -538,7 +538,7 @@ class Status final { // // * It may provide more fine-grained semantic information about the error // to facilitate actionable remedies. - // * It may provide human-readable contexual information that is more + // * It may provide human-readable contextual information that is more // appropriate to display to an end user. // // A payload consists of a [key,value] pair, where the key is a string @@ -886,6 +886,15 @@ inline Status OkStatus() { return Status(); } // message-less kCancelled errors are common in the infrastructure. inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); } +// Retrieves a message's status as a null terminated C string. The lifetime of +// this string is tied to the lifetime of the status object itself. +// +// If the status's message is empty, the empty string is returned. +// +// StatusMessageAsCStr exists for C support. Use `status.message()` in C++. +const char* StatusMessageAsCStr( + const Status& status ABSL_ATTRIBUTE_LIFETIME_BOUND); + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc index 74a64ace..898a9cb2 100644 --- a/absl/status/status_test.cc +++ b/absl/status/status_test.cc @@ -132,6 +132,29 @@ TEST(Status, ConstructorWithCodeMessage) { } } +TEST(Status, StatusMessageCStringTest) { + { + absl::Status status = absl::OkStatus(); + EXPECT_EQ(status.message(), ""); + EXPECT_STREQ(absl::StatusMessageAsCStr(status), ""); + EXPECT_EQ(status.message(), absl::StatusMessageAsCStr(status)); + EXPECT_NE(absl::StatusMessageAsCStr(status), nullptr); + } + { + absl::Status status; + EXPECT_EQ(status.message(), ""); + EXPECT_NE(absl::StatusMessageAsCStr(status), nullptr); + EXPECT_STREQ(absl::StatusMessageAsCStr(status), ""); + } + { + absl::Status status(absl::StatusCode::kInternal, "message"); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + EXPECT_EQ("message", status.message()); + EXPECT_STREQ("message", absl::StatusMessageAsCStr(status)); + } +} + TEST(Status, ConstructOutOfRangeCode) { const int kRawCode = 9999; absl::Status status(static_cast<absl::StatusCode>(kRawCode), ""); diff --git a/absl/status/statusor.h b/absl/status/statusor.h index a76e7201..54c7ce02 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -146,7 +146,7 @@ class ABSL_MUST_USE_RESULT StatusOr; // // absl::StatusOr<int> i = GetCount(); // if (i.ok()) { -// updated_total += *i +// updated_total += *i; // } // // NOTE: using `absl::StatusOr<T>::value()` when no valid value is present will @@ -411,7 +411,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, typename = typename std::enable_if<absl::conjunction< std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>, absl::disjunction< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, T>, + std::is_same<absl::remove_cvref_t<U>, T>, absl::conjunction< absl::negation<std::is_convertible<U&&, absl::Status>>, absl::negation<internal_statusor:: @@ -444,8 +444,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, internal_statusor::IsDirectInitializationValid<T, U&&>, std::is_constructible<T, U&&>, std::is_convertible<U&&, T>, absl::disjunction< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, - T>, + std::is_same<absl::remove_cvref_t<U>, T>, absl::conjunction< absl::negation<std::is_convertible<U&&, absl::Status>>, absl::negation< @@ -461,8 +460,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, absl::conjunction< internal_statusor::IsDirectInitializationValid<T, U&&>, absl::disjunction< - std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, - T>, + std::is_same<absl::remove_cvref_t<U>, T>, absl::conjunction< absl::negation<std::is_constructible<absl::Status, U&&>>, absl::negation< @@ -584,7 +582,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, // Reconstructs the inner value T in-place using the provided args, using the // T(args...) constructor. Returns reference to the reconstructed `T`. template <typename... Args> - T& emplace(Args&&... args) { + T& emplace(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { if (ok()) { this->Clear(); this->MakeValue(std::forward<Args>(args)...); @@ -600,7 +598,8 @@ class StatusOr : private internal_statusor::StatusOrData<T>, absl::enable_if_t< std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, int> = 0> - T& emplace(std::initializer_list<U> ilist, Args&&... args) { + T& emplace(std::initializer_list<U> ilist, + Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { if (ok()) { this->Clear(); this->MakeValue(ilist, std::forward<Args>(args)...); @@ -611,6 +610,21 @@ class StatusOr : private internal_statusor::StatusOrData<T>, return this->data_; } + // StatusOr<T>::AssignStatus() + // + // Sets the status of `absl::StatusOr<T>` to the given non-ok status value. + // + // NOTE: We recommend using the constructor and `operator=` where possible. + // This method is intended for use in generic programming, to enable setting + // the status of a `StatusOr<T>` when `T` may be `Status`. In that case, the + // constructor and `operator=` would assign into the inner value of type + // `Status`, rather than status of the `StatusOr` (b/280392796). + // + // REQUIRES: !Status(std::forward<U>(v)).ok(). This requirement is DCHECKed. + // In optimized builds, passing absl::OkStatus() here will have the effect + // of passing absl::StatusCode::kInternal as a fallback. + using internal_statusor::StatusOrData<T>::AssignStatus; + private: using internal_statusor::StatusOrData<T>::Assign; template <typename U> diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc index 29021543..e65f5d27 100644 --- a/absl/status/statusor_test.cc +++ b/absl/status/statusor_test.cc @@ -1844,4 +1844,37 @@ TEST(StatusOr, AssignmentFromTypeConvertibleToStatus) { } } +TEST(StatusOr, StatusAssignmentFromStatusError) { + absl::StatusOr<absl::Status> statusor; + statusor.AssignStatus(absl::CancelledError()); + + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), absl::CancelledError()); +} + +#if GTEST_HAS_DEATH_TEST +TEST(StatusOr, StatusAssignmentFromStatusOk) { + EXPECT_DEBUG_DEATH( + { + absl::StatusOr<absl::Status> statusor; + // This will DCHECK. + statusor.AssignStatus(absl::OkStatus()); + // In optimized mode, we are actually going to get error::INTERNAL for + // status here, rather than crashing, so check that. + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status().code(), absl::StatusCode::kInternal); + }, + "An OK status is not a valid constructor argument to StatusOr<T>"); +} +#endif + +TEST(StatusOr, StatusAssignmentFromTypeConvertibleToStatus) { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor.AssignStatus(v); + + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); +} + } // namespace |