Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,252 @@
// Copyright 2023 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/status/internal/status_internal.h"
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/base/nullability.h"
#include "absl/debugging/leak_check.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/status_payload_printer.h"
#include "absl/strings/cord.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace status_internal {
void StatusRep::Unref() const {
// Fast path: if ref==1, there is no need for a RefCountDec (since
// this is the only reference and therefore no other thread is
// allowed to be mucking with r).
if (ref_.load(std::memory_order_acquire) == 1 ||
ref_.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
delete this;
}
}
static absl::optional<size_t> FindPayloadIndexByUrl(
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;
}
return absl::nullopt;
}
absl::optional<absl::Cord> StatusRep::GetPayload(
absl::string_view type_url) const {
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
if (index.has_value()) return (*payloads_)[index.value()].payload;
return absl::nullopt;
}
void StatusRep::SetPayload(absl::string_view type_url, absl::Cord payload) {
if (payloads_ == nullptr) {
payloads_ = absl::make_unique<status_internal::Payloads>();
}
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
if (index.has_value()) {
(*payloads_)[index.value()].payload = std::move(payload);
return;
}
payloads_->push_back({std::string(type_url), std::move(payload)});
}
StatusRep::EraseResult StatusRep::ErasePayload(absl::string_view type_url) {
absl::optional<size_t> index =
status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
if (!index.has_value()) return {false, Status::PointerToRep(this)};
payloads_->erase(payloads_->begin() + index.value());
if (payloads_->empty() && message_.empty()) {
// Special case: If this can be represented inlined, it MUST be inlined
// (== depends on this behavior).
EraseResult result = {true, Status::CodeToInlinedRep(code_)};
Unref();
return result;
}
return {true, Status::PointerToRep(this)};
}
void StatusRep::ForEachPayload(
absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
const {
if (auto* payloads = payloads_.get()) {
bool in_reverse =
payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
for (size_t index = 0; index < payloads->size(); ++index) {
const auto& elem =
(*payloads)[in_reverse ? payloads->size() - 1 - index : index];
#ifdef NDEBUG
visitor(elem.type_url, elem.payload);
#else
// In debug mode invalidate the type url to prevent users from relying on
// this string lifetime.
// NOLINTNEXTLINE intentional extra conversion to force temporary.
visitor(std::string(elem.type_url), elem.payload);
#endif // NDEBUG
}
}
}
std::string StatusRep::ToString(StatusToStringMode mode) const {
std::string text;
absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
const bool with_payload = (mode & StatusToStringMode::kWithPayload) ==
StatusToStringMode::kWithPayload;
if (with_payload) {
status_internal::StatusPayloadPrinter printer =
status_internal::GetStatusPayloadPrinter();
this->ForEachPayload([&](absl::string_view type_url,
const absl::Cord& payload) {
absl::optional<std::string> result;
if (printer) result = printer(type_url, payload);
absl::StrAppend(
&text, " [", type_url, "='",
result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
"']");
});
}
return text;
}
bool StatusRep::operator==(const StatusRep& other) const {
assert(this != &other);
if (code_ != other.code_) return false;
if (message_ != other.message_) return false;
const status_internal::Payloads* this_payloads = payloads_.get();
const status_internal::Payloads* other_payloads = other.payloads_.get();
const status_internal::Payloads no_payloads;
const status_internal::Payloads* larger_payloads =
this_payloads ? this_payloads : &no_payloads;
const status_internal::Payloads* smaller_payloads =
other_payloads ? other_payloads : &no_payloads;
if (larger_payloads->size() < smaller_payloads->size()) {
std::swap(larger_payloads, smaller_payloads);
}
if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
// Payloads can be ordered differently, so we can't just compare payload
// vectors.
for (const auto& payload : *larger_payloads) {
bool found = false;
for (const auto& other_payload : *smaller_payloads) {
if (payload.type_url == other_payload.type_url) {
if (payload.payload != other_payload.payload) {
return false;
}
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
absl::Nonnull<StatusRep*> StatusRep::CloneAndUnref() const {
// Optimization: no need to create a clone if we already have a refcount of 1.
if (ref_.load(std::memory_order_acquire) == 1) {
// All StatusRep instances are heap allocated and mutable, therefore this
// const_cast will never cast away const from a stack instance.
//
// CloneAndUnref is the only method that doesn't involve an external cast to
// get a mutable StatusRep* from the uintptr_t rep stored in Status.
return const_cast<StatusRep*>(this);
}
std::unique_ptr<status_internal::Payloads> payloads;
if (payloads_) {
payloads = absl::make_unique<status_internal::Payloads>(*payloads_);
}
auto* new_rep = new StatusRep(code_, message_, std::move(payloads));
Unref();
return new_rep;
}
// Convert canonical code to a value known to this binary.
absl::StatusCode MapToLocalCode(int value) {
absl::StatusCode code = static_cast<absl::StatusCode>(value);
switch (code) {
case absl::StatusCode::kOk:
case absl::StatusCode::kCancelled:
case absl::StatusCode::kUnknown:
case absl::StatusCode::kInvalidArgument:
case absl::StatusCode::kDeadlineExceeded:
case absl::StatusCode::kNotFound:
case absl::StatusCode::kAlreadyExists:
case absl::StatusCode::kPermissionDenied:
case absl::StatusCode::kResourceExhausted:
case absl::StatusCode::kFailedPrecondition:
case absl::StatusCode::kAborted:
case absl::StatusCode::kOutOfRange:
case absl::StatusCode::kUnimplemented:
case absl::StatusCode::kInternal:
case absl::StatusCode::kUnavailable:
case absl::StatusCode::kDataLoss:
case absl::StatusCode::kUnauthenticated:
return code;
default:
return absl::StatusCode::kUnknown;
}
}
absl::Nonnull<const char*> MakeCheckFailString(
absl::Nonnull<const absl::Status*> status,
absl::Nonnull<const char*> prefix) {
// There's no need to free this string since the process is crashing.
return absl::IgnoreLeak(
new std::string(absl::StrCat(
prefix, " (",
status->ToString(StatusToStringMode::kWithEverything), ")")))
->c_str();
}
} // namespace status_internal
ABSL_NAMESPACE_END
} // namespace absl

View file

@ -0,0 +1,132 @@
// Copyright 2019 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
#define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
#include <atomic>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/nullability.h"
#include "absl/container/inlined_vector.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#ifndef SWIG
// Disabled for SWIG as it doesn't parse attributes correctly.
namespace absl {
ABSL_NAMESPACE_BEGIN
// Returned Status objects may not be ignored. Codesearch doesn't handle ifdefs
// as part of a class definitions (b/6995610), so we use a forward declaration.
//
// TODO(b/176172494): ABSL_MUST_USE_RESULT should expand to the more strict
// [[nodiscard]]. For now, just use [[nodiscard]] directly when it is available.
#if ABSL_HAVE_CPP_ATTRIBUTE(nodiscard)
class [[nodiscard]] ABSL_ATTRIBUTE_TRIVIAL_ABI Status;
#else
class ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_TRIVIAL_ABI Status;
#endif
ABSL_NAMESPACE_END
} // namespace absl
#endif // !SWIG
namespace absl {
ABSL_NAMESPACE_BEGIN
enum class StatusCode : int;
enum class StatusToStringMode : int;
namespace status_internal {
// Container for status payloads.
struct Payload {
std::string type_url;
absl::Cord payload;
};
using Payloads = absl::InlinedVector<Payload, 1>;
// Reference-counted representation of Status data.
class StatusRep {
public:
StatusRep(absl::StatusCode code_arg, absl::string_view message_arg,
std::unique_ptr<status_internal::Payloads> payloads_arg)
: ref_(int32_t{1}),
code_(code_arg),
message_(message_arg),
payloads_(std::move(payloads_arg)) {}
absl::StatusCode code() const { return code_; }
const std::string& message() const { return message_; }
// Ref and unref are const to allow access through a const pointer, and are
// used during copying operations.
void Ref() const { ref_.fetch_add(1, std::memory_order_relaxed); }
void Unref() const;
// Payload methods correspond to the same methods in absl::Status.
absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
void SetPayload(absl::string_view type_url, absl::Cord payload);
struct EraseResult {
bool erased;
uintptr_t new_rep;
};
EraseResult ErasePayload(absl::string_view type_url);
void ForEachPayload(
absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
const;
std::string ToString(StatusToStringMode mode) const;
bool operator==(const StatusRep& other) const;
bool operator!=(const StatusRep& other) const { return !(*this == other); }
// Returns an equivalent heap allocated StatusRep with refcount 1.
//
// `this` is not safe to be used after calling as it may have been deleted.
absl::Nonnull<StatusRep*> CloneAndUnref() const;
private:
mutable 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_;
};
absl::StatusCode MapToLocalCode(int value);
// Returns a pointer to a newly-allocated string with the given `prefix`,
// suitable for output as an error message in assertion/`CHECK()` failures.
//
// This is an internal implementation detail for Abseil logging.
ABSL_ATTRIBUTE_PURE_FUNCTION
absl::Nonnull<const char*> MakeCheckFailString(
absl::Nonnull<const absl::Status*> status,
absl::Nonnull<const char*> prefix);
} // namespace status_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_

View file

@ -0,0 +1,68 @@
// Copyright 2024 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: status_matchers.cc
// -----------------------------------------------------------------------------
#include "absl/status/internal/status_matchers.h"
#include <ostream>
#include <string>
#include "gmock/gmock.h" // gmock_for_status_matchers.h
#include "absl/base/config.h"
#include "absl/status/status.h"
namespace absl_testing {
ABSL_NAMESPACE_BEGIN
namespace status_internal {
void StatusIsMatcherCommonImpl::DescribeTo(std::ostream* os) const {
*os << ", has a status code that ";
code_matcher_.DescribeTo(os);
*os << ", and has an error message that ";
message_matcher_.DescribeTo(os);
}
void StatusIsMatcherCommonImpl::DescribeNegationTo(std::ostream* os) const {
*os << ", or has a status code that ";
code_matcher_.DescribeNegationTo(os);
*os << ", or has an error message that ";
message_matcher_.DescribeNegationTo(os);
}
bool StatusIsMatcherCommonImpl::MatchAndExplain(
const ::absl::Status& status,
::testing::MatchResultListener* result_listener) const {
::testing::StringMatchResultListener inner_listener;
if (!code_matcher_.MatchAndExplain(status.code(), &inner_listener)) {
*result_listener << (inner_listener.str().empty()
? "whose status code is wrong"
: "which has a status code " +
inner_listener.str());
return false;
}
if (!message_matcher_.Matches(std::string(status.message()))) {
*result_listener << "whose error message is wrong";
return false;
}
return true;
}
} // namespace status_internal
ABSL_NAMESPACE_END
} // namespace absl_testing

View file

@ -0,0 +1,246 @@
// Copyright 2024 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_
#define ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_
#include <ostream> // NOLINT
#include <string>
#include <type_traits>
#include <utility>
#include "gmock/gmock.h" // gmock_for_status_matchers.h
#include "absl/base/config.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
namespace absl_testing {
ABSL_NAMESPACE_BEGIN
namespace status_internal {
inline const absl::Status& GetStatus(const absl::Status& status) {
return status;
}
template <typename T>
inline const absl::Status& GetStatus(const absl::StatusOr<T>& status) {
return status.status();
}
////////////////////////////////////////////////////////////
// Implementation of IsOkAndHolds().
// Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a
// reference to StatusOr<T>.
template <typename StatusOrType>
class IsOkAndHoldsMatcherImpl
: public ::testing::MatcherInterface<StatusOrType> {
public:
typedef
typename std::remove_reference<StatusOrType>::type::value_type value_type;
template <typename InnerMatcher>
explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher)
: inner_matcher_(::testing::SafeMatcherCast<const value_type&>(
std::forward<InnerMatcher>(inner_matcher))) {}
void DescribeTo(std::ostream* os) const override {
*os << "is OK and has a value that ";
inner_matcher_.DescribeTo(os);
}
void DescribeNegationTo(std::ostream* os) const override {
*os << "isn't OK or has a value that ";
inner_matcher_.DescribeNegationTo(os);
}
bool MatchAndExplain(
StatusOrType actual_value,
::testing::MatchResultListener* result_listener) const override {
if (!GetStatus(actual_value).ok()) {
*result_listener << "which has status " << GetStatus(actual_value);
return false;
}
// Call through to the inner matcher.
return inner_matcher_.MatchAndExplain(*actual_value, result_listener);
}
private:
const ::testing::Matcher<const value_type&> inner_matcher_;
};
// Implements IsOkAndHolds(m) as a polymorphic matcher.
template <typename InnerMatcher>
class IsOkAndHoldsMatcher {
public:
explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher)
: inner_matcher_(std::forward<InnerMatcher>(inner_matcher)) {}
// Converts this polymorphic matcher to a monomorphic matcher of the
// given type. StatusOrType can be either StatusOr<T> or a
// reference to StatusOr<T>.
template <typename StatusOrType>
operator ::testing::Matcher<StatusOrType>() const { // NOLINT
return ::testing::Matcher<StatusOrType>(
new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_));
}
private:
const InnerMatcher inner_matcher_;
};
////////////////////////////////////////////////////////////
// Implementation of StatusIs().
// `StatusCode` is implicitly convertible from `int`, `absl::StatusCode`, and
// is explicitly convertible to these types as well.
//
// We need this class because `absl::StatusCode` (as a scoped enum) is not
// implicitly convertible to `int`. In order to handle use cases like
// ```
// StatusIs(Anyof(absl::StatusCode::kUnknown, absl::StatusCode::kCancelled))
// ```
// which uses polymorphic matchers, we need to unify the interfaces into
// `Matcher<StatusCode>`.
class StatusCode {
public:
/*implicit*/ StatusCode(int code) // NOLINT
: code_(static_cast<::absl::StatusCode>(code)) {}
/*implicit*/ StatusCode(::absl::StatusCode code) : code_(code) {} // NOLINT
explicit operator int() const { return static_cast<int>(code_); }
friend inline void PrintTo(const StatusCode& code, std::ostream* os) {
// TODO(b/321095377): Change this to print the status code as a string.
*os << static_cast<int>(code);
}
private:
::absl::StatusCode code_;
};
// Relational operators to handle matchers like Eq, Lt, etc..
inline bool operator==(const StatusCode& lhs, const StatusCode& rhs) {
return static_cast<int>(lhs) == static_cast<int>(rhs);
}
inline bool operator!=(const StatusCode& lhs, const StatusCode& rhs) {
return static_cast<int>(lhs) != static_cast<int>(rhs);
}
// StatusIs() is a polymorphic matcher. This class is the common
// implementation of it shared by all types T where StatusIs() can be
// used as a Matcher<T>.
class StatusIsMatcherCommonImpl {
public:
StatusIsMatcherCommonImpl(
::testing::Matcher<StatusCode> code_matcher,
::testing::Matcher<absl::string_view> message_matcher)
: code_matcher_(std::move(code_matcher)),
message_matcher_(std::move(message_matcher)) {}
void DescribeTo(std::ostream* os) const;
void DescribeNegationTo(std::ostream* os) const;
bool MatchAndExplain(const absl::Status& status,
::testing::MatchResultListener* result_listener) const;
private:
const ::testing::Matcher<StatusCode> code_matcher_;
const ::testing::Matcher<absl::string_view> message_matcher_;
};
// Monomorphic implementation of matcher StatusIs() for a given type
// T. T can be Status, StatusOr<>, or a reference to either of them.
template <typename T>
class MonoStatusIsMatcherImpl : public ::testing::MatcherInterface<T> {
public:
explicit MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl)
: common_impl_(std::move(common_impl)) {}
void DescribeTo(std::ostream* os) const override {
common_impl_.DescribeTo(os);
}
void DescribeNegationTo(std::ostream* os) const override {
common_impl_.DescribeNegationTo(os);
}
bool MatchAndExplain(
T actual_value,
::testing::MatchResultListener* result_listener) const override {
return common_impl_.MatchAndExplain(GetStatus(actual_value),
result_listener);
}
private:
StatusIsMatcherCommonImpl common_impl_;
};
// Implements StatusIs() as a polymorphic matcher.
class StatusIsMatcher {
public:
template <typename StatusCodeMatcher, typename StatusMessageMatcher>
StatusIsMatcher(StatusCodeMatcher&& code_matcher,
StatusMessageMatcher&& message_matcher)
: common_impl_(::testing::MatcherCast<StatusCode>(
std::forward<StatusCodeMatcher>(code_matcher)),
::testing::MatcherCast<absl::string_view>(
std::forward<StatusMessageMatcher>(message_matcher))) {
}
// Converts this polymorphic matcher to a monomorphic matcher of the
// given type. T can be StatusOr<>, Status, or a reference to
// either of them.
template <typename T>
/*implicit*/ operator ::testing::Matcher<T>() const { // NOLINT
return ::testing::Matcher<T>(
new MonoStatusIsMatcherImpl<const T&>(common_impl_));
}
private:
const StatusIsMatcherCommonImpl common_impl_;
};
// Monomorphic implementation of matcher IsOk() for a given type T.
// T can be Status, StatusOr<>, or a reference to either of them.
template <typename T>
class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> {
public:
void DescribeTo(std::ostream* os) const override { *os << "is OK"; }
void DescribeNegationTo(std::ostream* os) const override {
*os << "is not OK";
}
bool MatchAndExplain(T actual_value,
::testing::MatchResultListener*) const override {
return GetStatus(actual_value).ok();
}
};
// Implements IsOk() as a polymorphic matcher.
class IsOkMatcher {
public:
template <typename T>
/*implicit*/ operator ::testing::Matcher<T>() const { // NOLINT
return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<const T&>());
}
};
} // namespace status_internal
ABSL_NAMESPACE_END
} // namespace absl_testing
#endif // ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_

View file

@ -0,0 +1,494 @@
// Copyright 2020 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
#define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
#include <cstdint>
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/nullability.h"
#include "absl/meta/type_traits.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/utility/utility.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
template <typename T>
class ABSL_MUST_USE_RESULT StatusOr;
namespace internal_statusor {
// Detects whether `U` has conversion operator to `StatusOr<T>`, i.e. `operator
// StatusOr<T>()`.
template <typename T, typename U, typename = void>
struct HasConversionOperatorToStatusOr : std::false_type {};
template <typename T, typename U>
void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]);
template <typename T, typename U>
struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))>
: std::true_type {};
// Detects whether `T` is constructible or convertible from `StatusOr<U>`.
template <typename T, typename U>
using IsConstructibleOrConvertibleFromStatusOr =
absl::disjunction<std::is_constructible<T, StatusOr<U>&>,
std::is_constructible<T, const StatusOr<U>&>,
std::is_constructible<T, StatusOr<U>&&>,
std::is_constructible<T, const StatusOr<U>&&>,
std::is_convertible<StatusOr<U>&, T>,
std::is_convertible<const StatusOr<U>&, T>,
std::is_convertible<StatusOr<U>&&, T>,
std::is_convertible<const StatusOr<U>&&, T>>;
// Detects whether `T` is constructible or convertible or assignable from
// `StatusOr<U>`.
template <typename T, typename U>
using IsConstructibleOrConvertibleOrAssignableFromStatusOr =
absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>,
std::is_assignable<T&, StatusOr<U>&>,
std::is_assignable<T&, const StatusOr<U>&>,
std::is_assignable<T&, StatusOr<U>&&>,
std::is_assignable<T&, const StatusOr<U>&&>>;
// Detects whether direct initializing `StatusOr<T>` from `U` is ambiguous, i.e.
// when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`.
template <typename T, typename U>
struct IsDirectInitializationAmbiguous
: public absl::conditional_t<
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>>
: public IsConstructibleOrConvertibleFromStatusOr<T, V> {};
// Checks against the constraints of the direction initialization, i.e. when
// `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution.
template <typename T, typename U>
using IsDirectInitializationValid = absl::disjunction<
// Short circuits if T is basically U.
std::is_same<T, absl::remove_cvref_t<U>>,
absl::negation<absl::disjunction<
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
// is equivalent to whether all the following conditions are met:
// 1. `U` is `StatusOr<V>`.
// 2. `T` is constructible and assignable from `V`.
// 3. `T` is constructible and assignable from `U` (i.e. `StatusOr<V>`).
// For example, the following code is considered ambiguous:
// (`T` is `bool`, `U` is `StatusOr<bool>`, `V` is `bool`)
// StatusOr<bool> s1 = true; // s1.ok() && s1.ValueOrDie() == true
// StatusOr<bool> s2 = false; // s2.ok() && s2.ValueOrDie() == false
// s1 = s2; // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`?
template <typename T, typename U>
struct IsForwardingAssignmentAmbiguous
: public absl::conditional_t<
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>>
: public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {};
// Checks against the constraints of the forwarding assignment, i.e. whether
// `StatusOr<T>::operator(U&&)` should participate in overload resolution.
template <typename T, typename U>
using IsForwardingAssignmentValid = absl::disjunction<
// Short circuits if T is basically U.
std::is_same<T, absl::remove_cvref_t<U>>,
absl::negation<absl::disjunction<
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>>>>;
template <bool Value, typename T>
using Equality = std::conditional_t<Value, T, absl::negation<T>>;
template <bool Explicit, typename T, typename U, bool Lifetimebound>
using IsConstructionValid = absl::conjunction<
Equality<Lifetimebound,
type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
IsDirectInitializationValid<T, U&&>, std::is_constructible<T, U&&>,
Equality<!Explicit, std::is_convertible<U&&, T>>,
absl::disjunction<
std::is_same<T, absl::remove_cvref_t<U>>,
absl::conjunction<
std::conditional_t<
Explicit,
absl::negation<std::is_constructible<absl::Status, U&&>>,
absl::negation<std::is_convertible<U&&, absl::Status>>>,
absl::negation<
internal_statusor::HasConversionOperatorToStatusOr<T, U&&>>>>>;
template <typename T, typename U, bool Lifetimebound>
using IsAssignmentValid = absl::conjunction<
Equality<Lifetimebound,
type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>,
absl::disjunction<
std::is_same<T, absl::remove_cvref_t<U>>,
absl::conjunction<
absl::negation<std::is_convertible<U&&, absl::Status>>,
absl::negation<HasConversionOperatorToStatusOr<T, U&&>>>>,
IsForwardingAssignmentValid<T, U&&>>;
template <bool Explicit, typename T, typename U>
using IsConstructionFromStatusValid = absl::conjunction<
absl::negation<std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>>,
absl::negation<std::is_same<T, absl::remove_cvref_t<U>>>,
absl::negation<std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>>,
Equality<!Explicit, std::is_convertible<U, absl::Status>>,
std::is_constructible<absl::Status, U>,
absl::negation<HasConversionOperatorToStatusOr<T, U>>>;
template <bool Explicit, typename T, typename U, bool Lifetimebound,
typename UQ>
using IsConstructionFromStatusOrValid = absl::conjunction<
absl::negation<std::is_same<T, U>>,
Equality<Lifetimebound,
type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
std::is_constructible<T, UQ>,
Equality<!Explicit, std::is_convertible<UQ, T>>,
absl::negation<IsConstructibleOrConvertibleFromStatusOr<T, U>>>;
template <typename T, typename U, bool Lifetimebound>
using IsStatusOrAssignmentValid = absl::conjunction<
absl::negation<std::is_same<T, absl::remove_cvref_t<U>>>,
Equality<Lifetimebound,
type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
std::is_constructible<T, U>, std::is_assignable<T, U>,
absl::negation<IsConstructibleOrConvertibleOrAssignableFromStatusOr<
T, absl::remove_cvref_t<U>>>>;
class Helper {
public:
// Move type-agnostic error handling to the .cc.
static void HandleInvalidStatusCtorArg(absl::Nonnull<Status*>);
[[noreturn]] static void Crash(const absl::Status& status);
};
// Construct an instance of T in `p` through placement new, passing Args... to
// the constructor.
// This abstraction is here mostly for the gcc performance fix.
template <typename T, typename... Args>
ABSL_ATTRIBUTE_NONNULL(1)
void PlacementNew(absl::Nonnull<void*> p, Args&&... args) {
new (p) T(std::forward<Args>(args)...);
}
// Helper base class to hold the data and all operations.
// We move all this to a base class to allow mixing with the appropriate
// TraitsBase specialization.
template <typename T>
class StatusOrData {
template <typename U>
friend class StatusOrData;
public:
StatusOrData() = delete;
StatusOrData(const StatusOrData& other) {
if (other.ok()) {
MakeValue(other.data_);
MakeStatus();
} else {
MakeStatus(other.status_);
}
}
StatusOrData(StatusOrData&& other) noexcept {
if (other.ok()) {
MakeValue(std::move(other.data_));
MakeStatus();
} else {
MakeStatus(std::move(other.status_));
}
}
template <typename U>
explicit StatusOrData(const StatusOrData<U>& other) {
if (other.ok()) {
MakeValue(other.data_);
MakeStatus();
} else {
MakeStatus(other.status_);
}
}
template <typename U>
explicit StatusOrData(StatusOrData<U>&& other) {
if (other.ok()) {
MakeValue(std::move(other.data_));
MakeStatus();
} else {
MakeStatus(std::move(other.status_));
}
}
template <typename... Args>
explicit StatusOrData(absl::in_place_t, Args&&... args)
: data_(std::forward<Args>(args)...) {
MakeStatus();
}
explicit StatusOrData(const T& value) : data_(value) {
MakeStatus();
}
explicit StatusOrData(T&& value) : data_(std::move(value)) {
MakeStatus();
}
template <typename U,
absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value,
int> = 0>
explicit StatusOrData(U&& v) : status_(std::forward<U>(v)) {
EnsureNotOk();
}
StatusOrData& operator=(const StatusOrData& other) {
if (this == &other) return *this;
if (other.ok())
Assign(other.data_);
else
AssignStatus(other.status_);
return *this;
}
StatusOrData& operator=(StatusOrData&& other) {
if (this == &other) return *this;
if (other.ok())
Assign(std::move(other.data_));
else
AssignStatus(std::move(other.status_));
return *this;
}
~StatusOrData() {
if (ok()) {
status_.~Status();
data_.~T();
} else {
status_.~Status();
}
}
template <typename U>
void Assign(U&& value) {
if (ok()) {
data_ = std::forward<U>(value);
} else {
MakeValue(std::forward<U>(value));
status_ = OkStatus();
}
}
template <typename U>
void AssignStatus(U&& v) {
Clear();
status_ = static_cast<absl::Status>(std::forward<U>(v));
EnsureNotOk();
}
bool ok() const { return status_.ok(); }
protected:
// status_ will always be active after the constructor.
// We make it a union to be able to initialize exactly how we need without
// waste.
// Eg. in the copy constructor we use the default constructor of Status in
// the ok() path to avoid an extra Ref call.
union {
Status status_;
};
// data_ is active iff status_.ok()==true
struct Dummy {};
union {
// When T is const, we need some non-const object we can cast to void* for
// the placement new. dummy_ is that object.
Dummy dummy_;
T data_;
};
void Clear() {
if (ok()) data_.~T();
}
void EnsureOk() const {
if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_);
}
void EnsureNotOk() {
if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_);
}
// Construct the value (ie. data_) through placement new with the passed
// argument.
template <typename... Arg>
void MakeValue(Arg&&... arg) {
internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...);
}
// Construct the status (ie. status_) through placement new with the passed
// argument.
template <typename... Args>
void MakeStatus(Args&&... args) {
internal_statusor::PlacementNew<Status>(&status_,
std::forward<Args>(args)...);
}
};
// Helper base classes to allow implicitly deleted constructors and assignment
// operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete
// the copy constructor when T is not copy constructible and `StatusOr` will
// inherit that behavior implicitly.
template <typename T, bool = std::is_copy_constructible<T>::value>
struct CopyCtorBase {
CopyCtorBase() = default;
CopyCtorBase(const CopyCtorBase&) = default;
CopyCtorBase(CopyCtorBase&&) = default;
CopyCtorBase& operator=(const CopyCtorBase&) = default;
CopyCtorBase& operator=(CopyCtorBase&&) = default;
};
template <typename T>
struct CopyCtorBase<T, false> {
CopyCtorBase() = default;
CopyCtorBase(const CopyCtorBase&) = delete;
CopyCtorBase(CopyCtorBase&&) = default;
CopyCtorBase& operator=(const CopyCtorBase&) = default;
CopyCtorBase& operator=(CopyCtorBase&&) = default;
};
template <typename T, bool = std::is_move_constructible<T>::value>
struct MoveCtorBase {
MoveCtorBase() = default;
MoveCtorBase(const MoveCtorBase&) = default;
MoveCtorBase(MoveCtorBase&&) = default;
MoveCtorBase& operator=(const MoveCtorBase&) = default;
MoveCtorBase& operator=(MoveCtorBase&&) = default;
};
template <typename T>
struct MoveCtorBase<T, false> {
MoveCtorBase() = default;
MoveCtorBase(const MoveCtorBase&) = default;
MoveCtorBase(MoveCtorBase&&) = delete;
MoveCtorBase& operator=(const MoveCtorBase&) = default;
MoveCtorBase& operator=(MoveCtorBase&&) = default;
};
template <typename T, bool = std::is_copy_constructible<T>::value&&
std::is_copy_assignable<T>::value>
struct CopyAssignBase {
CopyAssignBase() = default;
CopyAssignBase(const CopyAssignBase&) = default;
CopyAssignBase(CopyAssignBase&&) = default;
CopyAssignBase& operator=(const CopyAssignBase&) = default;
CopyAssignBase& operator=(CopyAssignBase&&) = default;
};
template <typename T>
struct CopyAssignBase<T, false> {
CopyAssignBase() = default;
CopyAssignBase(const CopyAssignBase&) = default;
CopyAssignBase(CopyAssignBase&&) = default;
CopyAssignBase& operator=(const CopyAssignBase&) = delete;
CopyAssignBase& operator=(CopyAssignBase&&) = default;
};
template <typename T, bool = std::is_move_constructible<T>::value&&
std::is_move_assignable<T>::value>
struct MoveAssignBase {
MoveAssignBase() = default;
MoveAssignBase(const MoveAssignBase&) = default;
MoveAssignBase(MoveAssignBase&&) = default;
MoveAssignBase& operator=(const MoveAssignBase&) = default;
MoveAssignBase& operator=(MoveAssignBase&&) = default;
};
template <typename T>
struct MoveAssignBase<T, false> {
MoveAssignBase() = default;
MoveAssignBase(const MoveAssignBase&) = default;
MoveAssignBase(MoveAssignBase&&) = default;
MoveAssignBase& operator=(const MoveAssignBase&) = default;
MoveAssignBase& operator=(MoveAssignBase&&) = delete;
};
[[noreturn]] void ThrowBadStatusOrAccess(absl::Status status);
// Used to introduce jitter into the output of printing functions for
// `StatusOr` (i.e. `AbslStringify` and `operator<<`).
class StringifyRandom {
enum BracesType {
kBareParens = 0,
kSpaceParens,
kBareBrackets,
kSpaceBrackets,
};
// Returns a random `BracesType` determined once per binary load.
static BracesType RandomBraces() {
static const BracesType kRandomBraces = static_cast<BracesType>(
(reinterpret_cast<uintptr_t>(&kRandomBraces) >> 4) % 4);
return kRandomBraces;
}
public:
static inline absl::string_view OpenBrackets() {
switch (RandomBraces()) {
case kBareParens:
return "(";
case kSpaceParens:
return "( ";
case kBareBrackets:
return "[";
case kSpaceBrackets:
return "[ ";
}
return "(";
}
static inline absl::string_view CloseBrackets() {
switch (RandomBraces()) {
case kBareParens:
return ")";
case kSpaceParens:
return " )";
case kBareBrackets:
return "]";
case kSpaceBrackets:
return " ]";
}
return ")";
}
};
} // namespace internal_statusor
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_

View file

@ -0,0 +1,421 @@
// Copyright 2019 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/status/status.h"
#include <errno.h>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <ostream>
#include <string>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/strerror.h"
#include "absl/base/macros.h"
#include "absl/base/no_destructor.h"
#include "absl/base/nullability.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
#include "absl/status/internal/status_internal.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
static_assert(
alignof(status_internal::StatusRep) >= 4,
"absl::Status assumes it can use the bottom 2 bits of a StatusRep*.");
std::string StatusCodeToString(StatusCode code) {
switch (code) {
case StatusCode::kOk:
return "OK";
case StatusCode::kCancelled:
return "CANCELLED";
case StatusCode::kUnknown:
return "UNKNOWN";
case StatusCode::kInvalidArgument:
return "INVALID_ARGUMENT";
case StatusCode::kDeadlineExceeded:
return "DEADLINE_EXCEEDED";
case StatusCode::kNotFound:
return "NOT_FOUND";
case StatusCode::kAlreadyExists:
return "ALREADY_EXISTS";
case StatusCode::kPermissionDenied:
return "PERMISSION_DENIED";
case StatusCode::kUnauthenticated:
return "UNAUTHENTICATED";
case StatusCode::kResourceExhausted:
return "RESOURCE_EXHAUSTED";
case StatusCode::kFailedPrecondition:
return "FAILED_PRECONDITION";
case StatusCode::kAborted:
return "ABORTED";
case StatusCode::kOutOfRange:
return "OUT_OF_RANGE";
case StatusCode::kUnimplemented:
return "UNIMPLEMENTED";
case StatusCode::kInternal:
return "INTERNAL";
case StatusCode::kUnavailable:
return "UNAVAILABLE";
case StatusCode::kDataLoss:
return "DATA_LOSS";
default:
return "";
}
}
std::ostream& operator<<(std::ostream& os, StatusCode code) {
return os << StatusCodeToString(code);
}
absl::Nonnull<const std::string*> Status::EmptyString() {
static const absl::NoDestructor<std::string> kEmpty;
return kEmpty.get();
}
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
constexpr const char Status::kMovedFromString[];
#endif
absl::Nonnull<const std::string*> Status::MovedFromString() {
static const absl::NoDestructor<std::string> kMovedFrom(kMovedFromString);
return kMovedFrom.get();
}
Status::Status(absl::StatusCode code, absl::string_view msg)
: rep_(CodeToInlinedRep(code)) {
if (code != absl::StatusCode::kOk && !msg.empty()) {
rep_ = PointerToRep(new status_internal::StatusRep(code, msg, nullptr));
}
}
absl::Nonnull<status_internal::StatusRep*> Status::PrepareToModify(
uintptr_t rep) {
if (IsInlined(rep)) {
return new status_internal::StatusRep(InlinedRepToCode(rep),
absl::string_view(), nullptr);
}
return RepToPointer(rep)->CloneAndUnref();
}
std::string Status::ToStringSlow(uintptr_t rep, StatusToStringMode mode) {
if (IsInlined(rep)) {
return absl::StrCat(absl::StatusCodeToString(InlinedRepToCode(rep)), ": ");
}
return RepToPointer(rep)->ToString(mode);
}
std::ostream& operator<<(std::ostream& os, const Status& x) {
os << x.ToString(StatusToStringMode::kWithEverything);
return os;
}
Status AbortedError(absl::string_view message) {
return Status(absl::StatusCode::kAborted, message);
}
Status AlreadyExistsError(absl::string_view message) {
return Status(absl::StatusCode::kAlreadyExists, message);
}
Status CancelledError(absl::string_view message) {
return Status(absl::StatusCode::kCancelled, message);
}
Status DataLossError(absl::string_view message) {
return Status(absl::StatusCode::kDataLoss, message);
}
Status DeadlineExceededError(absl::string_view message) {
return Status(absl::StatusCode::kDeadlineExceeded, message);
}
Status FailedPreconditionError(absl::string_view message) {
return Status(absl::StatusCode::kFailedPrecondition, message);
}
Status InternalError(absl::string_view message) {
return Status(absl::StatusCode::kInternal, message);
}
Status InvalidArgumentError(absl::string_view message) {
return Status(absl::StatusCode::kInvalidArgument, message);
}
Status NotFoundError(absl::string_view message) {
return Status(absl::StatusCode::kNotFound, message);
}
Status OutOfRangeError(absl::string_view message) {
return Status(absl::StatusCode::kOutOfRange, message);
}
Status PermissionDeniedError(absl::string_view message) {
return Status(absl::StatusCode::kPermissionDenied, message);
}
Status ResourceExhaustedError(absl::string_view message) {
return Status(absl::StatusCode::kResourceExhausted, message);
}
Status UnauthenticatedError(absl::string_view message) {
return Status(absl::StatusCode::kUnauthenticated, message);
}
Status UnavailableError(absl::string_view message) {
return Status(absl::StatusCode::kUnavailable, message);
}
Status UnimplementedError(absl::string_view message) {
return Status(absl::StatusCode::kUnimplemented, message);
}
Status UnknownError(absl::string_view message) {
return Status(absl::StatusCode::kUnknown, message);
}
bool IsAborted(const Status& status) {
return status.code() == absl::StatusCode::kAborted;
}
bool IsAlreadyExists(const Status& status) {
return status.code() == absl::StatusCode::kAlreadyExists;
}
bool IsCancelled(const Status& status) {
return status.code() == absl::StatusCode::kCancelled;
}
bool IsDataLoss(const Status& status) {
return status.code() == absl::StatusCode::kDataLoss;
}
bool IsDeadlineExceeded(const Status& status) {
return status.code() == absl::StatusCode::kDeadlineExceeded;
}
bool IsFailedPrecondition(const Status& status) {
return status.code() == absl::StatusCode::kFailedPrecondition;
}
bool IsInternal(const Status& status) {
return status.code() == absl::StatusCode::kInternal;
}
bool IsInvalidArgument(const Status& status) {
return status.code() == absl::StatusCode::kInvalidArgument;
}
bool IsNotFound(const Status& status) {
return status.code() == absl::StatusCode::kNotFound;
}
bool IsOutOfRange(const Status& status) {
return status.code() == absl::StatusCode::kOutOfRange;
}
bool IsPermissionDenied(const Status& status) {
return status.code() == absl::StatusCode::kPermissionDenied;
}
bool IsResourceExhausted(const Status& status) {
return status.code() == absl::StatusCode::kResourceExhausted;
}
bool IsUnauthenticated(const Status& status) {
return status.code() == absl::StatusCode::kUnauthenticated;
}
bool IsUnavailable(const Status& status) {
return status.code() == absl::StatusCode::kUnavailable;
}
bool IsUnimplemented(const Status& status) {
return status.code() == absl::StatusCode::kUnimplemented;
}
bool IsUnknown(const Status& status) {
return status.code() == absl::StatusCode::kUnknown;
}
StatusCode ErrnoToStatusCode(int error_number) {
switch (error_number) {
case 0:
return StatusCode::kOk;
case EINVAL: // Invalid argument
case ENAMETOOLONG: // Filename too long
case E2BIG: // Argument list too long
case EDESTADDRREQ: // Destination address required
case EDOM: // Mathematics argument out of domain of function
case EFAULT: // Bad address
case EILSEQ: // Illegal byte sequence
case ENOPROTOOPT: // Protocol not available
case ENOTSOCK: // Not a socket
case ENOTTY: // Inappropriate I/O control operation
case EPROTOTYPE: // Protocol wrong type for socket
case ESPIPE: // Invalid seek
return StatusCode::kInvalidArgument;
case ETIMEDOUT: // Connection timed out
return StatusCode::kDeadlineExceeded;
case ENODEV: // No such device
case ENOENT: // No such file or directory
#ifdef ENOMEDIUM
case ENOMEDIUM: // No medium found
#endif
case ENXIO: // No such device or address
case ESRCH: // No such process
return StatusCode::kNotFound;
case EEXIST: // File exists
case EADDRNOTAVAIL: // Address not available
case EALREADY: // Connection already in progress
#ifdef ENOTUNIQ
case ENOTUNIQ: // Name not unique on network
#endif
return StatusCode::kAlreadyExists;
case EPERM: // Operation not permitted
case EACCES: // Permission denied
#ifdef ENOKEY
case ENOKEY: // Required key not available
#endif
case EROFS: // Read only file system
return StatusCode::kPermissionDenied;
case ENOTEMPTY: // Directory not empty
case EISDIR: // Is a directory
case ENOTDIR: // Not a directory
case EADDRINUSE: // Address already in use
case EBADF: // Invalid file descriptor
#ifdef EBADFD
case EBADFD: // File descriptor in bad state
#endif
case EBUSY: // Device or resource busy
case ECHILD: // No child processes
case EISCONN: // Socket is connected
#ifdef EISNAM
case EISNAM: // Is a named type file
#endif
#ifdef ENOTBLK
case ENOTBLK: // Block device required
#endif
case ENOTCONN: // The socket is not connected
case EPIPE: // Broken pipe
#ifdef ESHUTDOWN
case ESHUTDOWN: // Cannot send after transport endpoint shutdown
#endif
case ETXTBSY: // Text file busy
#ifdef EUNATCH
case EUNATCH: // Protocol driver not attached
#endif
return StatusCode::kFailedPrecondition;
case ENOSPC: // No space left on device
#ifdef EDQUOT
case EDQUOT: // Disk quota exceeded
#endif
case EMFILE: // Too many open files
case EMLINK: // Too many links
case ENFILE: // Too many open files in system
case ENOBUFS: // No buffer space available
case ENOMEM: // Not enough space
#ifdef EUSERS
case EUSERS: // Too many users
#endif
return StatusCode::kResourceExhausted;
#ifdef ECHRNG
case ECHRNG: // Channel number out of range
#endif
case EFBIG: // File too large
case EOVERFLOW: // Value too large to be stored in data type
case ERANGE: // Result too large
return StatusCode::kOutOfRange;
#ifdef ENOPKG
case ENOPKG: // Package not installed
#endif
case ENOSYS: // Function not implemented
case ENOTSUP: // Operation not supported
case EAFNOSUPPORT: // Address family not supported
#ifdef EPFNOSUPPORT
case EPFNOSUPPORT: // Protocol family not supported
#endif
case EPROTONOSUPPORT: // Protocol not supported
#ifdef ESOCKTNOSUPPORT
case ESOCKTNOSUPPORT: // Socket type not supported
#endif
case EXDEV: // Improper link
return StatusCode::kUnimplemented;
case EAGAIN: // Resource temporarily unavailable
#ifdef ECOMM
case ECOMM: // Communication error on send
#endif
case ECONNREFUSED: // Connection refused
case ECONNABORTED: // Connection aborted
case ECONNRESET: // Connection reset
case EINTR: // Interrupted function call
#ifdef EHOSTDOWN
case EHOSTDOWN: // Host is down
#endif
case EHOSTUNREACH: // Host is unreachable
case ENETDOWN: // Network is down
case ENETRESET: // Connection aborted by network
case ENETUNREACH: // Network unreachable
case ENOLCK: // No locks available
case ENOLINK: // Link has been severed
#ifdef ENONET
case ENONET: // Machine is not on the network
#endif
return StatusCode::kUnavailable;
case EDEADLK: // Resource deadlock avoided
#ifdef ESTALE
case ESTALE: // Stale file handle
#endif
return StatusCode::kAborted;
case ECANCELED: // Operation cancelled
return StatusCode::kCancelled;
default:
return StatusCode::kUnknown;
}
}
namespace {
std::string MessageForErrnoToStatus(int error_number,
absl::string_view message) {
return absl::StrCat(message, ": ",
absl::base_internal::StrError(error_number));
}
} // namespace
Status ErrnoToStatus(int error_number, absl::string_view message) {
return Status(ErrnoToStatusCode(error_number),
MessageForErrnoToStatus(error_number, message));
}
absl::Nonnull<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

View file

@ -0,0 +1,943 @@
// Copyright 2019 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: status.h
// -----------------------------------------------------------------------------
//
// This header file defines the Abseil `status` library, consisting of:
//
// * An `absl::Status` class for holding error handling information
// * A set of canonical `absl::StatusCode` error codes, and associated
// utilities for generating and propagating status codes.
// * A set of helper functions for creating status codes and checking their
// values
//
// Within Google, `absl::Status` is the primary mechanism for communicating
// errors in C++, and is used to represent error state in both in-process
// library calls as well as RPC calls. Some of these errors may be recoverable,
// but others may not. Most functions that can produce a recoverable error
// should be designed to return an `absl::Status` (or `absl::StatusOr`).
//
// Example:
//
// absl::Status myFunction(absl::string_view fname, ...) {
// ...
// // encounter error
// if (error condition) {
// return absl::InvalidArgumentError("bad mode");
// }
// // else, return OK
// return absl::OkStatus();
// }
//
// An `absl::Status` is designed to either return "OK" or one of a number of
// different error codes, corresponding to typical error conditions.
// In almost all cases, when using `absl::Status` you should use the canonical
// error codes (of type `absl::StatusCode`) enumerated in this header file.
// These canonical codes are understood across the codebase and will be
// accepted across all API and RPC boundaries.
#ifndef ABSL_STATUS_STATUS_H_
#define ABSL_STATUS_STATUS_H_
#include <cassert>
#include <cstdint>
#include <ostream>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/base/nullability.h"
#include "absl/base/optimization.h"
#include "absl/functional/function_ref.h"
#include "absl/status/internal/status_internal.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
// absl::StatusCode
//
// An `absl::StatusCode` is an enumerated type indicating either no error ("OK")
// or an error condition. In most cases, an `absl::Status` indicates a
// recoverable error, and the purpose of signalling an error is to indicate what
// action to take in response to that error. These error codes map to the proto
// RPC error codes indicated in https://cloud.google.com/apis/design/errors.
//
// The errors listed below are the canonical errors associated with
// `absl::Status` and are used throughout the codebase. As a result, these
// error codes are somewhat generic.
//
// In general, try to return the most specific error that applies if more than
// one error may pertain. For example, prefer `kOutOfRange` over
// `kFailedPrecondition` if both codes apply. Similarly prefer `kNotFound` or
// `kAlreadyExists` over `kFailedPrecondition`.
//
// Because these errors may cross RPC boundaries, these codes are tied to the
// `google.rpc.Code` definitions within
// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
// The string value of these RPC codes is denoted within each enum below.
//
// If your error handling code requires more context, you can attach payloads
// to your status. See `absl::Status::SetPayload()` and
// `absl::Status::GetPayload()` below.
enum class StatusCode : int {
// StatusCode::kOk
//
// kOK (gRPC code "OK") does not indicate an error; this value is returned on
// success. It is typical to check for this value before proceeding on any
// given call across an API or RPC boundary. To check this value, use the
// `absl::Status::ok()` member function rather than inspecting the raw code.
kOk = 0,
// StatusCode::kCancelled
//
// kCancelled (gRPC code "CANCELLED") indicates the operation was cancelled,
// typically by the caller.
kCancelled = 1,
// StatusCode::kUnknown
//
// kUnknown (gRPC code "UNKNOWN") indicates an unknown error occurred. In
// general, more specific errors should be raised, if possible. Errors raised
// by APIs that do not return enough error information may be converted to
// this error.
kUnknown = 2,
// StatusCode::kInvalidArgument
//
// kInvalidArgument (gRPC code "INVALID_ARGUMENT") indicates the caller
// specified an invalid argument, such as a malformed filename. Note that use
// of such errors should be narrowly limited to indicate the invalid nature of
// the arguments themselves. Errors with validly formed arguments that may
// cause errors with the state of the receiving system should be denoted with
// `kFailedPrecondition` instead.
kInvalidArgument = 3,
// StatusCode::kDeadlineExceeded
//
// kDeadlineExceeded (gRPC code "DEADLINE_EXCEEDED") indicates a deadline
// expired before the operation could complete. For operations that may change
// state within a system, this error may be returned even if the operation has
// completed successfully. For example, a successful response from a server
// could have been delayed long enough for the deadline to expire.
kDeadlineExceeded = 4,
// StatusCode::kNotFound
//
// kNotFound (gRPC code "NOT_FOUND") indicates some requested entity (such as
// a file or directory) was not found.
//
// `kNotFound` is useful if a request should be denied for an entire class of
// users, such as during a gradual feature rollout or undocumented allow list.
// If a request should be denied for specific sets of users, such as through
// user-based access control, use `kPermissionDenied` instead.
kNotFound = 5,
// StatusCode::kAlreadyExists
//
// kAlreadyExists (gRPC code "ALREADY_EXISTS") indicates that the entity a
// caller attempted to create (such as a file or directory) is already
// present.
kAlreadyExists = 6,
// StatusCode::kPermissionDenied
//
// kPermissionDenied (gRPC code "PERMISSION_DENIED") indicates that the caller
// does not have permission to execute the specified operation. Note that this
// error is different than an error due to an *un*authenticated user. This
// error code does not imply the request is valid or the requested entity
// exists or satisfies any other pre-conditions.
//
// `kPermissionDenied` must not be used for rejections caused by exhausting
// some resource. Instead, use `kResourceExhausted` for those errors.
// `kPermissionDenied` must not be used if the caller cannot be identified.
// Instead, use `kUnauthenticated` for those errors.
kPermissionDenied = 7,
// StatusCode::kResourceExhausted
//
// kResourceExhausted (gRPC code "RESOURCE_EXHAUSTED") indicates some resource
// has been exhausted, perhaps a per-user quota, or perhaps the entire file
// system is out of space.
kResourceExhausted = 8,
// StatusCode::kFailedPrecondition
//
// kFailedPrecondition (gRPC code "FAILED_PRECONDITION") indicates that the
// operation was rejected because the system is not in a state required for
// the operation's execution. For example, a directory to be deleted may be
// non-empty, an "rmdir" operation is applied to a non-directory, etc.
//
// Some guidelines that may help a service implementer in deciding between
// `kFailedPrecondition`, `kAborted`, and `kUnavailable`:
//
// (a) Use `kUnavailable` if the client can retry just the failing call.
// (b) Use `kAborted` if the client should retry at a higher transaction
// level (such as when a client-specified test-and-set fails, indicating
// the client should restart a read-modify-write sequence).
// (c) Use `kFailedPrecondition` if the client should not retry until
// the system state has been explicitly fixed. For example, if a "rmdir"
// fails because the directory is non-empty, `kFailedPrecondition`
// should be returned since the client should not retry unless
// the files are deleted from the directory.
kFailedPrecondition = 9,
// StatusCode::kAborted
//
// kAborted (gRPC code "ABORTED") indicates the operation was aborted,
// typically due to a concurrency issue such as a sequencer check failure or a
// failed transaction.
//
// See the guidelines above for deciding between `kFailedPrecondition`,
// `kAborted`, and `kUnavailable`.
kAborted = 10,
// StatusCode::kOutOfRange
//
// kOutOfRange (gRPC code "OUT_OF_RANGE") indicates the operation was
// attempted past the valid range, such as seeking or reading past an
// end-of-file.
//
// Unlike `kInvalidArgument`, this error indicates a problem that may
// be fixed if the system state changes. For example, a 32-bit file
// system will generate `kInvalidArgument` if asked to read at an
// offset that is not in the range [0,2^32-1], but it will generate
// `kOutOfRange` if asked to read from an offset past the current
// file size.
//
// There is a fair bit of overlap between `kFailedPrecondition` and
// `kOutOfRange`. We recommend using `kOutOfRange` (the more specific
// error) when it applies so that callers who are iterating through
// a space can easily look for an `kOutOfRange` error to detect when
// they are done.
kOutOfRange = 11,
// StatusCode::kUnimplemented
//
// kUnimplemented (gRPC code "UNIMPLEMENTED") indicates the operation is not
// implemented or supported in this service. In this case, the operation
// should not be re-attempted.
kUnimplemented = 12,
// StatusCode::kInternal
//
// kInternal (gRPC code "INTERNAL") indicates an internal error has occurred
// and some invariants expected by the underlying system have not been
// satisfied. This error code is reserved for serious errors.
kInternal = 13,
// StatusCode::kUnavailable
//
// kUnavailable (gRPC code "UNAVAILABLE") indicates the service is currently
// unavailable and that this is most likely a transient condition. An error
// such as this can be corrected by retrying with a backoff scheme. Note that
// it is not always safe to retry non-idempotent operations.
//
// See the guidelines above for deciding between `kFailedPrecondition`,
// `kAborted`, and `kUnavailable`.
kUnavailable = 14,
// StatusCode::kDataLoss
//
// kDataLoss (gRPC code "DATA_LOSS") indicates that unrecoverable data loss or
// corruption has occurred. As this error is serious, proper alerting should
// be attached to errors such as this.
kDataLoss = 15,
// StatusCode::kUnauthenticated
//
// kUnauthenticated (gRPC code "UNAUTHENTICATED") indicates that the request
// does not have valid authentication credentials for the operation. Correct
// the authentication and try again.
kUnauthenticated = 16,
// StatusCode::DoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_
//
// NOTE: this error code entry should not be used and you should not rely on
// its value, which may change.
//
// The purpose of this enumerated value is to force people who handle status
// codes with `switch()` statements to *not* simply enumerate all possible
// values, but instead provide a "default:" case. Providing such a default
// case ensures that code will compile when new codes are added.
kDoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20
};
// StatusCodeToString()
//
// Returns the name for the status code, or "" if it is an unknown value.
std::string StatusCodeToString(StatusCode code);
// operator<<
//
// Streams StatusCodeToString(code) to `os`.
std::ostream& operator<<(std::ostream& os, StatusCode code);
// absl::StatusToStringMode
//
// An `absl::StatusToStringMode` is an enumerated type indicating how
// `absl::Status::ToString()` should construct the output string for a non-ok
// status.
enum class StatusToStringMode : int {
// ToString will not contain any extra data (such as payloads). It will only
// contain the error code and message, if any.
kWithNoExtraData = 0,
// ToString will contain the payloads.
kWithPayload = 1 << 0,
// ToString will include all the extra data this Status has.
kWithEverything = ~kWithNoExtraData,
// Default mode used by ToString. Its exact value might change in the future.
kDefault = kWithPayload,
};
// absl::StatusToStringMode is specified as a bitmask type, which means the
// following operations must be provided:
inline constexpr StatusToStringMode operator&(StatusToStringMode lhs,
StatusToStringMode rhs) {
return static_cast<StatusToStringMode>(static_cast<int>(lhs) &
static_cast<int>(rhs));
}
inline constexpr StatusToStringMode operator|(StatusToStringMode lhs,
StatusToStringMode rhs) {
return static_cast<StatusToStringMode>(static_cast<int>(lhs) |
static_cast<int>(rhs));
}
inline constexpr StatusToStringMode operator^(StatusToStringMode lhs,
StatusToStringMode rhs) {
return static_cast<StatusToStringMode>(static_cast<int>(lhs) ^
static_cast<int>(rhs));
}
inline constexpr StatusToStringMode operator~(StatusToStringMode arg) {
return static_cast<StatusToStringMode>(~static_cast<int>(arg));
}
inline StatusToStringMode& operator&=(StatusToStringMode& lhs,
StatusToStringMode rhs) {
lhs = lhs & rhs;
return lhs;
}
inline StatusToStringMode& operator|=(StatusToStringMode& lhs,
StatusToStringMode rhs) {
lhs = lhs | rhs;
return lhs;
}
inline StatusToStringMode& operator^=(StatusToStringMode& lhs,
StatusToStringMode rhs) {
lhs = lhs ^ rhs;
return lhs;
}
// absl::Status
//
// The `absl::Status` class is generally used to gracefully handle errors
// across API boundaries (and in particular across RPC boundaries). Some of
// these errors may be recoverable, but others may not. Most
// functions which can produce a recoverable error should be designed to return
// either an `absl::Status` (or the similar `absl::StatusOr<T>`, which holds
// either an object of type `T` or an error).
//
// API developers should construct their functions to return `absl::OkStatus()`
// upon success, or an `absl::StatusCode` upon another type of error (e.g
// an `absl::StatusCode::kInvalidArgument` error). The API provides convenience
// functions to construct each status code.
//
// Example:
//
// absl::Status myFunction(absl::string_view fname, ...) {
// ...
// // encounter error
// if (error condition) {
// // Construct an absl::StatusCode::kInvalidArgument error
// return absl::InvalidArgumentError("bad mode");
// }
// // else, return OK
// return absl::OkStatus();
// }
//
// Users handling status error codes should prefer checking for an OK status
// using the `ok()` member function. Handling multiple error codes may justify
// use of switch statement, but only check for error codes you know how to
// handle; do not try to exhaustively match against all canonical error codes.
// Errors that cannot be handled should be logged and/or propagated for higher
// levels to deal with. If you do use a switch statement, make sure that you
// also provide a `default:` switch case, so that code does not break as other
// canonical codes are added to the API.
//
// Example:
//
// absl::Status result = DoSomething();
// if (!result.ok()) {
// LOG(ERROR) << result;
// }
//
// // Provide a default if switching on multiple error codes
// switch (result.code()) {
// // The user hasn't authenticated. Ask them to reauth
// case absl::StatusCode::kUnauthenticated:
// DoReAuth();
// break;
// // The user does not have permission. Log an error.
// case absl::StatusCode::kPermissionDenied:
// LOG(ERROR) << result;
// break;
// // Propagate the error otherwise.
// default:
// return true;
// }
//
// An `absl::Status` can optionally include a payload with more information
// about the error. Typically, this payload serves one of several purposes:
//
// * It may provide more fine-grained semantic information about the error to
// facilitate actionable remedies.
// * It may provide human-readable contextual information that is more
// appropriate to display to an end user.
//
// Example:
//
// absl::Status result = DoSomething();
// // Inform user to retry after 30 seconds
// // See more error details in googleapis/google/rpc/error_details.proto
// if (absl::IsResourceExhausted(result)) {
// google::rpc::RetryInfo info;
// info.retry_delay().seconds() = 30;
// // Payloads require a unique key (a URL to ensure no collisions with
// // other payloads), and an `absl::Cord` to hold the encoded data.
// absl::string_view url = "type.googleapis.com/google.rpc.RetryInfo";
// result.SetPayload(url, info.SerializeAsCord());
// return result;
// }
//
// For documentation see https://abseil.io/docs/cpp/guides/status.
//
// Returned Status objects may not be ignored. status_internal.h has a forward
// declaration of the form
// class ABSL_MUST_USE_RESULT Status;
class ABSL_ATTRIBUTE_TRIVIAL_ABI Status final {
public:
// Constructors
// This default constructor creates an OK status with no message or payload.
// Avoid this constructor and prefer explicit construction of an OK status
// with `absl::OkStatus()`.
Status();
// Creates a status in the canonical error space with the specified
// `absl::StatusCode` and error message. If `code == absl::StatusCode::kOk`, // NOLINT
// `msg` is ignored and an object identical to an OK status is constructed.
//
// The `msg` string must be in UTF-8. The implementation may complain (e.g., // NOLINT
// by printing a warning) if it is not.
Status(absl::StatusCode code, absl::string_view msg);
Status(const Status&);
Status& operator=(const Status& x);
// Move operators
// The moved-from state is valid but unspecified.
Status(Status&&) noexcept;
Status& operator=(Status&&) noexcept;
~Status();
// Status::Update()
//
// Updates the existing status with `new_status` provided that `this->ok()`.
// If the existing status already contains a non-OK error, this update has no
// effect and preserves the current data. Note that this behavior may change
// in the future to augment a current non-ok status with additional
// information about `new_status`.
//
// `Update()` provides a convenient way of keeping track of the first error
// encountered.
//
// Example:
// // Instead of "if (overall_status.ok()) overall_status = new_status"
// overall_status.Update(new_status);
//
void Update(const Status& new_status);
void Update(Status&& new_status);
// Status::ok()
//
// Returns `true` if `this->code()` == `absl::StatusCode::kOk`,
// indicating the absence of an error.
// Prefer checking for an OK status using this member function.
ABSL_MUST_USE_RESULT bool ok() const;
// Status::code()
//
// Returns the canonical error code of type `absl::StatusCode` of this status.
absl::StatusCode code() const;
// Status::raw_code()
//
// Returns a raw (canonical) error code corresponding to the enum value of
// `google.rpc.Code` definitions within
// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto.
// These values could be out of the range of canonical `absl::StatusCode`
// enum values.
//
// NOTE: This function should only be called when converting to an associated
// wire format. Use `Status::code()` for error handling.
int raw_code() const;
// Status::message()
//
// Returns the error message associated with this error code, if available.
// Note that this message rarely describes the error code. It is not unusual
// for the error message to be the empty string. As a result, prefer
// `operator<<` or `Status::ToString()` for debug logging.
absl::string_view message() const;
friend bool operator==(const Status&, const Status&);
friend bool operator!=(const Status&, const Status&);
// Status::ToString()
//
// Returns a string based on the `mode`. By default, it returns combination of
// the error code name, the message and any associated payload messages. This
// string is designed simply to be human readable and its exact format should
// not be load bearing. Do not depend on the exact format of the result of
// `ToString()` which is subject to change.
//
// The printed code name and the message are generally substrings of the
// result, and the payloads to be printed use the status payload printer
// mechanism (which is internal).
std::string ToString(
StatusToStringMode mode = StatusToStringMode::kDefault) const;
// Support `absl::StrCat`, `absl::StrFormat`, etc.
template <typename Sink>
friend void AbslStringify(Sink& sink, const Status& status) {
sink.Append(status.ToString(StatusToStringMode::kWithEverything));
}
// Status::IgnoreError()
//
// Ignores any errors. This method does nothing except potentially suppress
// complaints from any tools that are checking that errors are not dropped on
// the floor.
void IgnoreError() const;
// swap()
//
// Swap the contents of one status with another.
friend void swap(Status& a, Status& b) noexcept;
//----------------------------------------------------------------------------
// Payload Management APIs
//----------------------------------------------------------------------------
// A payload may be attached to a status to provide additional context to an
// error that may not be satisfied by an existing `absl::StatusCode`.
// Typically, this payload serves one of several purposes:
//
// * It may provide more fine-grained semantic information about the error
// to facilitate actionable remedies.
// * 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
// referring to a unique "type URL" and the value is an object of type
// `absl::Cord` to hold the contextual data.
//
// The "type URL" should be unique and follow the format of a URL
// (https://en.wikipedia.org/wiki/URL) and, ideally, provide some
// documentation or schema on how to interpret its associated data. For
// example, the default type URL for a protobuf message type is
// "type.googleapis.com/packagename.messagename". Other custom wire formats
// should define the format of type URL in a similar practice so as to
// minimize the chance of conflict between type URLs.
// Users should ensure that the type URL can be mapped to a concrete
// C++ type if they want to deserialize the payload and read it effectively.
//
// To attach a payload to a status object, call `Status::SetPayload()`,
// passing it the type URL and an `absl::Cord` of associated data. Similarly,
// to extract the payload from a status, call `Status::GetPayload()`. You
// may attach multiple payloads (with differing type URLs) to any given
// status object, provided that the status is currently exhibiting an error
// code (i.e. is not OK).
// Status::GetPayload()
//
// Gets the payload of a status given its unique `type_url` key, if present.
absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
// Status::SetPayload()
//
// Sets the payload for a non-ok status using a `type_url` key, overwriting
// any existing payload for that `type_url`.
//
// NOTE: This function does nothing if the Status is ok.
void SetPayload(absl::string_view type_url, absl::Cord payload);
// Status::ErasePayload()
//
// Erases the payload corresponding to the `type_url` key. Returns `true` if
// the payload was present.
bool ErasePayload(absl::string_view type_url);
// Status::ForEachPayload()
//
// Iterates over the stored payloads and calls the
// `visitor(type_key, payload)` callable for each one.
//
// NOTE: The order of calls to `visitor()` is not specified and may change at
// any time.
//
// NOTE: Any mutation on the same 'absl::Status' object during visitation is
// forbidden and could result in undefined behavior.
void ForEachPayload(
absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
const;
private:
friend Status CancelledError();
// Creates a status in the canonical error space with the specified
// code, and an empty error message.
explicit Status(absl::StatusCode code);
// Underlying constructor for status from a rep_.
explicit Status(uintptr_t rep) : rep_(rep) {}
static void Ref(uintptr_t rep);
static void Unref(uintptr_t rep);
// REQUIRES: !ok()
// Ensures rep is not inlined or shared with any other Status.
static absl::Nonnull<status_internal::StatusRep*> PrepareToModify(
uintptr_t rep);
// MSVC 14.0 limitation requires the const.
static constexpr const char kMovedFromString[] =
"Status accessed after move.";
static absl::Nonnull<const std::string*> EmptyString();
static absl::Nonnull<const std::string*> MovedFromString();
// Returns whether rep contains an inlined representation.
// See rep_ for details.
static constexpr bool IsInlined(uintptr_t rep);
// Indicates whether this Status was the rhs of a move operation. See rep_
// for details.
static constexpr bool IsMovedFrom(uintptr_t rep);
static constexpr uintptr_t MovedFromRep();
// Convert between error::Code and the inlined uintptr_t representation used
// by rep_. See rep_ for details.
static constexpr uintptr_t CodeToInlinedRep(absl::StatusCode code);
static constexpr absl::StatusCode InlinedRepToCode(uintptr_t rep);
// Converts between StatusRep* and the external uintptr_t representation used
// by rep_. See rep_ for details.
static uintptr_t PointerToRep(absl::Nonnull<status_internal::StatusRep*> r);
static absl::Nonnull<const status_internal::StatusRep*> RepToPointer(
uintptr_t r);
static std::string ToStringSlow(uintptr_t rep, StatusToStringMode mode);
// Status supports two different representations.
// - When the low bit is set it is an inlined representation.
// It uses the canonical error space, no message or payload.
// The error code is (rep_ >> 2).
// The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom().
// - When the low bit is off it is an external representation.
// In this case all the data comes from a heap allocated Rep object.
// rep_ is a status_internal::StatusRep* pointer to that structure.
uintptr_t rep_;
friend class status_internal::StatusRep;
};
// OkStatus()
//
// Returns an OK status, equivalent to a default constructed instance. Prefer
// usage of `absl::OkStatus()` when constructing such an OK status.
Status OkStatus();
// operator<<()
//
// Prints a human-readable representation of `x` to `os`.
std::ostream& operator<<(std::ostream& os, const Status& x);
// IsAborted()
// IsAlreadyExists()
// IsCancelled()
// IsDataLoss()
// IsDeadlineExceeded()
// IsFailedPrecondition()
// IsInternal()
// IsInvalidArgument()
// IsNotFound()
// IsOutOfRange()
// IsPermissionDenied()
// IsResourceExhausted()
// IsUnauthenticated()
// IsUnavailable()
// IsUnimplemented()
// IsUnknown()
//
// These convenience functions return `true` if a given status matches the
// `absl::StatusCode` error code of its associated function.
ABSL_MUST_USE_RESULT bool IsAborted(const Status& status);
ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status);
ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status);
ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status);
ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status);
ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status);
ABSL_MUST_USE_RESULT bool IsInternal(const Status& status);
ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status);
ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status);
ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status);
ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status);
ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status);
ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status);
ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status);
ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status);
ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status);
// AbortedError()
// AlreadyExistsError()
// CancelledError()
// DataLossError()
// DeadlineExceededError()
// FailedPreconditionError()
// InternalError()
// InvalidArgumentError()
// NotFoundError()
// OutOfRangeError()
// PermissionDeniedError()
// ResourceExhaustedError()
// UnauthenticatedError()
// UnavailableError()
// UnimplementedError()
// UnknownError()
//
// These convenience functions create an `absl::Status` object with an error
// code as indicated by the associated function name, using the error message
// passed in `message`.
Status AbortedError(absl::string_view message);
Status AlreadyExistsError(absl::string_view message);
Status CancelledError(absl::string_view message);
Status DataLossError(absl::string_view message);
Status DeadlineExceededError(absl::string_view message);
Status FailedPreconditionError(absl::string_view message);
Status InternalError(absl::string_view message);
Status InvalidArgumentError(absl::string_view message);
Status NotFoundError(absl::string_view message);
Status OutOfRangeError(absl::string_view message);
Status PermissionDeniedError(absl::string_view message);
Status ResourceExhaustedError(absl::string_view message);
Status UnauthenticatedError(absl::string_view message);
Status UnavailableError(absl::string_view message);
Status UnimplementedError(absl::string_view message);
Status UnknownError(absl::string_view message);
// ErrnoToStatusCode()
//
// Returns the StatusCode for `error_number`, which should be an `errno` value.
// See https://en.cppreference.com/w/cpp/error/errno_macros and similar
// references.
absl::StatusCode ErrnoToStatusCode(int error_number);
// ErrnoToStatus()
//
// Convenience function that creates a `absl::Status` using an `error_number`,
// which should be an `errno` value.
Status ErrnoToStatus(int error_number, absl::string_view message);
//------------------------------------------------------------------------------
// Implementation details follow
//------------------------------------------------------------------------------
inline Status::Status() : Status(absl::StatusCode::kOk) {}
inline Status::Status(absl::StatusCode code) : Status(CodeToInlinedRep(code)) {}
inline Status::Status(const Status& x) : Status(x.rep_) { Ref(rep_); }
inline Status& Status::operator=(const Status& x) {
uintptr_t old_rep = rep_;
if (x.rep_ != old_rep) {
Ref(x.rep_);
rep_ = x.rep_;
Unref(old_rep);
}
return *this;
}
inline Status::Status(Status&& x) noexcept : Status(x.rep_) {
x.rep_ = MovedFromRep();
}
inline Status& Status::operator=(Status&& x) noexcept {
uintptr_t old_rep = rep_;
if (x.rep_ != old_rep) {
rep_ = x.rep_;
x.rep_ = MovedFromRep();
Unref(old_rep);
}
return *this;
}
inline void Status::Update(const Status& new_status) {
if (ok()) {
*this = new_status;
}
}
inline void Status::Update(Status&& new_status) {
if (ok()) {
*this = std::move(new_status);
}
}
inline Status::~Status() { Unref(rep_); }
inline bool Status::ok() const {
return rep_ == CodeToInlinedRep(absl::StatusCode::kOk);
}
inline absl::StatusCode Status::code() const {
return status_internal::MapToLocalCode(raw_code());
}
inline int Status::raw_code() const {
if (IsInlined(rep_)) return static_cast<int>(InlinedRepToCode(rep_));
return static_cast<int>(RepToPointer(rep_)->code());
}
inline absl::string_view Status::message() const {
return !IsInlined(rep_)
? RepToPointer(rep_)->message()
: (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString)
: absl::string_view());
}
inline bool operator==(const Status& lhs, const Status& rhs) {
if (lhs.rep_ == rhs.rep_) return true;
if (Status::IsInlined(lhs.rep_)) return false;
if (Status::IsInlined(rhs.rep_)) return false;
return *Status::RepToPointer(lhs.rep_) == *Status::RepToPointer(rhs.rep_);
}
inline bool operator!=(const Status& lhs, const Status& rhs) {
return !(lhs == rhs);
}
inline std::string Status::ToString(StatusToStringMode mode) const {
return ok() ? "OK" : ToStringSlow(rep_, mode);
}
inline void Status::IgnoreError() const {
// no-op
}
inline void swap(absl::Status& a, absl::Status& b) noexcept {
using std::swap;
swap(a.rep_, b.rep_);
}
inline absl::optional<absl::Cord> Status::GetPayload(
absl::string_view type_url) const {
if (IsInlined(rep_)) return absl::nullopt;
return RepToPointer(rep_)->GetPayload(type_url);
}
inline void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
if (ok()) return;
status_internal::StatusRep* rep = PrepareToModify(rep_);
rep->SetPayload(type_url, std::move(payload));
rep_ = PointerToRep(rep);
}
inline bool Status::ErasePayload(absl::string_view type_url) {
if (IsInlined(rep_)) return false;
status_internal::StatusRep* rep = PrepareToModify(rep_);
auto res = rep->ErasePayload(type_url);
rep_ = res.new_rep;
return res.erased;
}
inline void Status::ForEachPayload(
absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
const {
if (IsInlined(rep_)) return;
RepToPointer(rep_)->ForEachPayload(visitor);
}
constexpr bool Status::IsInlined(uintptr_t rep) { return (rep & 1) != 0; }
constexpr bool Status::IsMovedFrom(uintptr_t rep) { return (rep & 2) != 0; }
constexpr uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
return (static_cast<uintptr_t>(code) << 2) + 1;
}
constexpr absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
ABSL_ASSERT(IsInlined(rep));
return static_cast<absl::StatusCode>(rep >> 2);
}
constexpr uintptr_t Status::MovedFromRep() {
return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
}
inline absl::Nonnull<const status_internal::StatusRep*> Status::RepToPointer(
uintptr_t rep) {
assert(!IsInlined(rep));
return reinterpret_cast<const status_internal::StatusRep*>(rep);
}
inline uintptr_t Status::PointerToRep(
absl::Nonnull<status_internal::StatusRep*> rep) {
return reinterpret_cast<uintptr_t>(rep);
}
inline void Status::Ref(uintptr_t rep) {
if (!IsInlined(rep)) RepToPointer(rep)->Ref();
}
inline void Status::Unref(uintptr_t rep) {
if (!IsInlined(rep)) RepToPointer(rep)->Unref();
}
inline Status OkStatus() { return Status(); }
// Creates a `Status` object with the `absl::StatusCode::kCancelled` error code
// and an empty message. It is provided only for efficiency, given that
// 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++.
absl::Nonnull<const char*> StatusMessageAsCStr(
const Status& status ABSL_ATTRIBUTE_LIFETIME_BOUND);
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STATUS_STATUS_H_

View file

@ -0,0 +1,118 @@
// Copyright 2024 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: status_matchers.h
// -----------------------------------------------------------------------------
//
// Testing utilities for working with `absl::Status` and `absl::StatusOr`.
//
// Defines the following utilities:
//
// ===============
// `IsOkAndHolds(m)`
// ===============
//
// This gMock matcher matches a StatusOr<T> value whose status is OK
// and whose inner value matches matcher m. Example:
//
// ```
// using ::testing::MatchesRegex;
// using ::absl_testing::IsOkAndHolds;
// ...
// absl::StatusOr<string> maybe_name = ...;
// EXPECT_THAT(maybe_name, IsOkAndHolds(MatchesRegex("John .*")));
// ```
//
// ===============================
// `StatusIs(status_code_matcher)`
// ===============================
//
// This is a shorthand for
// `StatusIs(status_code_matcher, ::testing::_)`
// In other words, it's like the two-argument `StatusIs()`, except that it
// ignores error message.
//
// ===============
// `IsOk()`
// ===============
//
// Matches an `absl::Status` or `absl::StatusOr<T>` value whose status value
// is `absl::StatusCode::kOk.`
//
// Equivalent to 'StatusIs(absl::StatusCode::kOk)'.
// Example:
// ```
// using ::absl_testing::IsOk;
// ...
// absl::StatusOr<string> maybe_name = ...;
// EXPECT_THAT(maybe_name, IsOk());
// Status s = ...;
// EXPECT_THAT(s, IsOk());
// ```
#ifndef ABSL_STATUS_STATUS_MATCHERS_H_
#define ABSL_STATUS_STATUS_MATCHERS_H_
#include <ostream> // NOLINT
#include <type_traits>
#include <utility>
#include "gmock/gmock.h" // gmock_for_status_matchers.h
#include "absl/base/config.h"
#include "absl/status/internal/status_matchers.h"
namespace absl_testing {
ABSL_NAMESPACE_BEGIN
// Returns a gMock matcher that matches a StatusOr<> whose status is
// OK and whose value matches the inner matcher.
template <typename InnerMatcherT>
status_internal::IsOkAndHoldsMatcher<typename std::decay<InnerMatcherT>::type>
IsOkAndHolds(InnerMatcherT&& inner_matcher) {
return status_internal::IsOkAndHoldsMatcher<
typename std::decay<InnerMatcherT>::type>(
std::forward<InnerMatcherT>(inner_matcher));
}
// Returns a gMock matcher that matches a Status or StatusOr<> whose status code
// matches code_matcher and whose error message matches message_matcher.
// Typically, code_matcher will be an absl::StatusCode, e.g.
//
// StatusIs(absl::StatusCode::kInvalidArgument, "...")
template <typename StatusCodeMatcherT, typename StatusMessageMatcherT>
status_internal::StatusIsMatcher StatusIs(
StatusCodeMatcherT&& code_matcher,
StatusMessageMatcherT&& message_matcher) {
return status_internal::StatusIsMatcher(
std::forward<StatusCodeMatcherT>(code_matcher),
std::forward<StatusMessageMatcherT>(message_matcher));
}
// Returns a gMock matcher that matches a Status or StatusOr<> and whose status
// code matches code_matcher. See above for details.
template <typename StatusCodeMatcherT>
status_internal::StatusIsMatcher StatusIs(StatusCodeMatcherT&& code_matcher) {
return StatusIs(std::forward<StatusCodeMatcherT>(code_matcher), ::testing::_);
}
// Returns a gMock matcher that matches a Status or StatusOr<> which is OK.
inline status_internal::IsOkMatcher IsOk() {
return status_internal::IsOkMatcher();
}
ABSL_NAMESPACE_END
} // namespace absl_testing
#endif // ABSL_STATUS_STATUS_MATCHERS_H_

View file

@ -0,0 +1,119 @@
// Copyright 2024 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: status_matchers_test.cc
// -----------------------------------------------------------------------------
#include "absl/status/status_matchers.h"
#include "gmock/gmock.h"
#include "gtest/gtest-spi.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
namespace {
using ::absl_testing::IsOk;
using ::absl_testing::IsOkAndHolds;
using ::absl_testing::StatusIs;
using ::testing::Gt;
TEST(StatusMatcherTest, StatusIsOk) { EXPECT_THAT(absl::OkStatus(), IsOk()); }
TEST(StatusMatcherTest, StatusOrIsOk) {
absl::StatusOr<int> ok_int = {0};
EXPECT_THAT(ok_int, IsOk());
}
TEST(StatusMatcherTest, StatusIsNotOk) {
absl::Status error = absl::UnknownError("Smigla");
EXPECT_NONFATAL_FAILURE(EXPECT_THAT(error, IsOk()), "Smigla");
}
TEST(StatusMatcherTest, StatusOrIsNotOk) {
absl::StatusOr<int> error = absl::UnknownError("Smigla");
EXPECT_NONFATAL_FAILURE(EXPECT_THAT(error, IsOk()), "Smigla");
}
TEST(StatusMatcherTest, IsOkAndHolds) {
absl::StatusOr<int> ok_int = {4};
absl::StatusOr<absl::string_view> ok_str = {"text"};
EXPECT_THAT(ok_int, IsOkAndHolds(4));
EXPECT_THAT(ok_int, IsOkAndHolds(Gt(0)));
EXPECT_THAT(ok_str, IsOkAndHolds("text"));
}
TEST(StatusMatcherTest, IsOkAndHoldsFailure) {
absl::StatusOr<int> ok_int = {502};
absl::StatusOr<int> error = absl::UnknownError("Smigla");
absl::StatusOr<absl::string_view> ok_str = {"actual"};
EXPECT_NONFATAL_FAILURE(EXPECT_THAT(ok_int, IsOkAndHolds(0)), "502");
EXPECT_NONFATAL_FAILURE(EXPECT_THAT(error, IsOkAndHolds(0)), "Smigla");
EXPECT_NONFATAL_FAILURE(EXPECT_THAT(ok_str, IsOkAndHolds("expected")),
"actual");
}
TEST(StatusMatcherTest, StatusIs) {
absl::Status unknown = absl::UnknownError("unbekannt");
absl::Status invalid = absl::InvalidArgumentError("ungueltig");
EXPECT_THAT(absl::OkStatus(), StatusIs(absl::StatusCode::kOk));
EXPECT_THAT(absl::OkStatus(), StatusIs(0));
EXPECT_THAT(unknown, StatusIs(absl::StatusCode::kUnknown));
EXPECT_THAT(unknown, StatusIs(2));
EXPECT_THAT(unknown, StatusIs(absl::StatusCode::kUnknown, "unbekannt"));
EXPECT_THAT(invalid, StatusIs(absl::StatusCode::kInvalidArgument));
EXPECT_THAT(invalid, StatusIs(3));
EXPECT_THAT(invalid,
StatusIs(absl::StatusCode::kInvalidArgument, "ungueltig"));
}
TEST(StatusMatcherTest, StatusOrIs) {
absl::StatusOr<int> ok = {42};
absl::StatusOr<int> unknown = absl::UnknownError("unbekannt");
absl::StatusOr<absl::string_view> invalid =
absl::InvalidArgumentError("ungueltig");
EXPECT_THAT(ok, StatusIs(absl::StatusCode::kOk));
EXPECT_THAT(ok, StatusIs(0));
EXPECT_THAT(unknown, StatusIs(absl::StatusCode::kUnknown));
EXPECT_THAT(unknown, StatusIs(2));
EXPECT_THAT(unknown, StatusIs(absl::StatusCode::kUnknown, "unbekannt"));
EXPECT_THAT(invalid, StatusIs(absl::StatusCode::kInvalidArgument));
EXPECT_THAT(invalid, StatusIs(3));
EXPECT_THAT(invalid,
StatusIs(absl::StatusCode::kInvalidArgument, "ungueltig"));
}
TEST(StatusMatcherTest, StatusIsFailure) {
absl::Status unknown = absl::UnknownError("unbekannt");
absl::Status invalid = absl::InvalidArgumentError("ungueltig");
EXPECT_NONFATAL_FAILURE(
EXPECT_THAT(absl::OkStatus(),
StatusIs(absl::StatusCode::kInvalidArgument)),
"OK");
EXPECT_NONFATAL_FAILURE(
EXPECT_THAT(unknown, StatusIs(absl::StatusCode::kCancelled)), "UNKNOWN");
EXPECT_NONFATAL_FAILURE(
EXPECT_THAT(unknown, StatusIs(absl::StatusCode::kUnknown, "inconnu")),
"unbekannt");
EXPECT_NONFATAL_FAILURE(
EXPECT_THAT(invalid, StatusIs(absl::StatusCode::kOutOfRange)), "INVALID");
EXPECT_NONFATAL_FAILURE(
EXPECT_THAT(invalid,
StatusIs(absl::StatusCode::kInvalidArgument, "invalide")),
"ungueltig");
}
} // namespace

View file

@ -0,0 +1,36 @@
// Copyright 2019 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/status/status_payload_printer.h"
#include "absl/base/config.h"
#include "absl/base/internal/atomic_hook.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace status_internal {
ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
static absl::base_internal::AtomicHook<StatusPayloadPrinter> storage;
void SetStatusPayloadPrinter(StatusPayloadPrinter printer) {
storage.Store(printer);
}
StatusPayloadPrinter GetStatusPayloadPrinter() {
return storage.Load();
}
} // namespace status_internal
ABSL_NAMESPACE_END
} // namespace absl

View file

@ -0,0 +1,52 @@
// Copyright 2019 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
#define ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
#include <string>
#include "absl/base/nullability.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace status_internal {
// By default, `Status::ToString` and `operator<<(Status)` print a payload by
// dumping the type URL and the raw bytes. To help debugging, we provide an
// extension point, which is a global printer function that can be set by users
// to specify how to print payloads. The function takes the type URL and the
// payload as input, and should return a valid human-readable string on success
// or `absl::nullopt` on failure (in which case it falls back to the default
// approach of printing the raw bytes).
// NOTE: This is an internal API and the design is subject to change in the
// future in a non-backward-compatible way. Since it's only meant for debugging
// purpose, you should not rely on it in any critical logic.
using StatusPayloadPrinter = absl::Nullable<absl::optional<std::string> (*)(
absl::string_view, const absl::Cord&)>;
// Sets the global payload printer. Only one printer should be set per process.
// If multiple printers are set, it's undefined which one will be used.
void SetStatusPayloadPrinter(StatusPayloadPrinter);
// Returns the global payload printer if previously set, otherwise `nullptr`.
StatusPayloadPrinter GetStatusPayloadPrinter();
} // namespace status_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_

View file

@ -0,0 +1,579 @@
// Copyright 2019 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/status/status.h"
#include <errno.h>
#include <array>
#include <cstddef>
#include <sstream>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
namespace {
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Optional;
using ::testing::UnorderedElementsAreArray;
TEST(StatusCode, InsertionOperator) {
const absl::StatusCode code = absl::StatusCode::kUnknown;
std::ostringstream oss;
oss << code;
EXPECT_EQ(oss.str(), absl::StatusCodeToString(code));
}
// This structure holds the details for testing a single error code,
// its creator, and its classifier.
struct ErrorTest {
absl::StatusCode code;
using Creator = absl::Status (*)(
absl::string_view
);
using Classifier = bool (*)(const absl::Status&);
Creator creator;
Classifier classifier;
};
constexpr ErrorTest kErrorTests[]{
{absl::StatusCode::kCancelled, absl::CancelledError, absl::IsCancelled},
{absl::StatusCode::kUnknown, absl::UnknownError, absl::IsUnknown},
{absl::StatusCode::kInvalidArgument, absl::InvalidArgumentError,
absl::IsInvalidArgument},
{absl::StatusCode::kDeadlineExceeded, absl::DeadlineExceededError,
absl::IsDeadlineExceeded},
{absl::StatusCode::kNotFound, absl::NotFoundError, absl::IsNotFound},
{absl::StatusCode::kAlreadyExists, absl::AlreadyExistsError,
absl::IsAlreadyExists},
{absl::StatusCode::kPermissionDenied, absl::PermissionDeniedError,
absl::IsPermissionDenied},
{absl::StatusCode::kResourceExhausted, absl::ResourceExhaustedError,
absl::IsResourceExhausted},
{absl::StatusCode::kFailedPrecondition, absl::FailedPreconditionError,
absl::IsFailedPrecondition},
{absl::StatusCode::kAborted, absl::AbortedError, absl::IsAborted},
{absl::StatusCode::kOutOfRange, absl::OutOfRangeError, absl::IsOutOfRange},
{absl::StatusCode::kUnimplemented, absl::UnimplementedError,
absl::IsUnimplemented},
{absl::StatusCode::kInternal, absl::InternalError, absl::IsInternal},
{absl::StatusCode::kUnavailable, absl::UnavailableError,
absl::IsUnavailable},
{absl::StatusCode::kDataLoss, absl::DataLossError, absl::IsDataLoss},
{absl::StatusCode::kUnauthenticated, absl::UnauthenticatedError,
absl::IsUnauthenticated},
};
TEST(Status, CreateAndClassify) {
for (const auto& test : kErrorTests) {
SCOPED_TRACE(absl::StatusCodeToString(test.code));
// Ensure that the creator does, in fact, create status objects with the
// expected error code and message.
std::string message =
absl::StrCat("error code ", test.code, " test message");
absl::Status status = test.creator(
message
);
EXPECT_EQ(test.code, status.code());
EXPECT_EQ(message, status.message());
// Ensure that the classifier returns true for a status produced by the
// creator.
EXPECT_TRUE(test.classifier(status));
// Ensure that the classifier returns false for status with a different
// code.
for (const auto& other : kErrorTests) {
if (other.code != test.code) {
EXPECT_FALSE(test.classifier(absl::Status(other.code, "")))
<< " other.code = " << other.code;
}
}
}
}
TEST(Status, DefaultConstructor) {
absl::Status status;
EXPECT_TRUE(status.ok());
EXPECT_EQ(absl::StatusCode::kOk, status.code());
EXPECT_EQ("", status.message());
}
TEST(Status, OkStatus) {
absl::Status status = absl::OkStatus();
EXPECT_TRUE(status.ok());
EXPECT_EQ(absl::StatusCode::kOk, status.code());
EXPECT_EQ("", status.message());
}
TEST(Status, ConstructorWithCodeMessage) {
{
absl::Status status(absl::StatusCode::kCancelled, "");
EXPECT_FALSE(status.ok());
EXPECT_EQ(absl::StatusCode::kCancelled, status.code());
EXPECT_EQ("", status.message());
}
{
absl::Status status(absl::StatusCode::kInternal, "message");
EXPECT_FALSE(status.ok());
EXPECT_EQ(absl::StatusCode::kInternal, status.code());
EXPECT_EQ("message", status.message());
}
}
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), "");
EXPECT_EQ(absl::StatusCode::kUnknown, status.code());
EXPECT_EQ(kRawCode, status.raw_code());
}
constexpr char kUrl1[] = "url.payload.1";
constexpr char kUrl2[] = "url.payload.2";
constexpr char kUrl3[] = "url.payload.3";
constexpr char kUrl4[] = "url.payload.xx";
constexpr char kPayload1[] = "aaaaa";
constexpr char kPayload2[] = "bbbbb";
constexpr char kPayload3[] = "ccccc";
using PayloadsVec = std::vector<std::pair<std::string, absl::Cord>>;
TEST(Status, TestGetSetPayload) {
absl::Status ok_status = absl::OkStatus();
ok_status.SetPayload(kUrl1, absl::Cord(kPayload1));
ok_status.SetPayload(kUrl2, absl::Cord(kPayload2));
EXPECT_FALSE(ok_status.GetPayload(kUrl1));
EXPECT_FALSE(ok_status.GetPayload(kUrl2));
absl::Status bad_status(absl::StatusCode::kInternal, "fail");
bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload1)));
EXPECT_THAT(bad_status.GetPayload(kUrl2), Optional(Eq(kPayload2)));
EXPECT_FALSE(bad_status.GetPayload(kUrl3));
bad_status.SetPayload(kUrl1, absl::Cord(kPayload3));
EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload3)));
// Testing dynamically generated type_url
bad_status.SetPayload(absl::StrCat(kUrl1, ".1"), absl::Cord(kPayload1));
EXPECT_THAT(bad_status.GetPayload(absl::StrCat(kUrl1, ".1")),
Optional(Eq(kPayload1)));
}
TEST(Status, TestErasePayload) {
absl::Status bad_status(absl::StatusCode::kInternal, "fail");
bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
bad_status.SetPayload(kUrl3, absl::Cord(kPayload3));
EXPECT_FALSE(bad_status.ErasePayload(kUrl4));
EXPECT_TRUE(bad_status.GetPayload(kUrl2));
EXPECT_TRUE(bad_status.ErasePayload(kUrl2));
EXPECT_FALSE(bad_status.GetPayload(kUrl2));
EXPECT_FALSE(bad_status.ErasePayload(kUrl2));
EXPECT_TRUE(bad_status.ErasePayload(kUrl1));
EXPECT_TRUE(bad_status.ErasePayload(kUrl3));
bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
EXPECT_TRUE(bad_status.ErasePayload(kUrl1));
}
TEST(Status, TestComparePayloads) {
absl::Status bad_status1(absl::StatusCode::kInternal, "fail");
bad_status1.SetPayload(kUrl1, absl::Cord(kPayload1));
bad_status1.SetPayload(kUrl2, absl::Cord(kPayload2));
bad_status1.SetPayload(kUrl3, absl::Cord(kPayload3));
absl::Status bad_status2(absl::StatusCode::kInternal, "fail");
bad_status2.SetPayload(kUrl2, absl::Cord(kPayload2));
bad_status2.SetPayload(kUrl3, absl::Cord(kPayload3));
bad_status2.SetPayload(kUrl1, absl::Cord(kPayload1));
EXPECT_EQ(bad_status1, bad_status2);
}
TEST(Status, TestComparePayloadsAfterErase) {
absl::Status payload_status(absl::StatusCode::kInternal, "");
payload_status.SetPayload(kUrl1, absl::Cord(kPayload1));
payload_status.SetPayload(kUrl2, absl::Cord(kPayload2));
absl::Status empty_status(absl::StatusCode::kInternal, "");
// Different payloads, not equal
EXPECT_NE(payload_status, empty_status);
EXPECT_TRUE(payload_status.ErasePayload(kUrl1));
// Still Different payloads, still not equal.
EXPECT_NE(payload_status, empty_status);
EXPECT_TRUE(payload_status.ErasePayload(kUrl2));
// Both empty payloads, should be equal
EXPECT_EQ(payload_status, empty_status);
}
PayloadsVec AllVisitedPayloads(const absl::Status& s) {
PayloadsVec result;
s.ForEachPayload([&](absl::string_view type_url, const absl::Cord& payload) {
result.push_back(std::make_pair(std::string(type_url), payload));
});
return result;
}
TEST(Status, TestForEachPayload) {
absl::Status bad_status(absl::StatusCode::kInternal, "fail");
bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
bad_status.SetPayload(kUrl3, absl::Cord(kPayload3));
int count = 0;
bad_status.ForEachPayload(
[&count](absl::string_view, const absl::Cord&) { ++count; });
EXPECT_EQ(count, 3);
PayloadsVec expected_payloads = {{kUrl1, absl::Cord(kPayload1)},
{kUrl2, absl::Cord(kPayload2)},
{kUrl3, absl::Cord(kPayload3)}};
// Test that we visit all the payloads in the status.
PayloadsVec visited_payloads = AllVisitedPayloads(bad_status);
EXPECT_THAT(visited_payloads, UnorderedElementsAreArray(expected_payloads));
// Test that visitation order is not consistent between run.
std::vector<absl::Status> scratch;
while (true) {
scratch.emplace_back(absl::StatusCode::kInternal, "fail");
scratch.back().SetPayload(kUrl1, absl::Cord(kPayload1));
scratch.back().SetPayload(kUrl2, absl::Cord(kPayload2));
scratch.back().SetPayload(kUrl3, absl::Cord(kPayload3));
if (AllVisitedPayloads(scratch.back()) != visited_payloads) {
break;
}
}
}
TEST(Status, ToString) {
absl::Status status(absl::StatusCode::kInternal, "fail");
EXPECT_EQ("INTERNAL: fail", status.ToString());
status.SetPayload("foo", absl::Cord("bar"));
EXPECT_EQ("INTERNAL: fail [foo='bar']", status.ToString());
status.SetPayload("bar", absl::Cord("\377"));
EXPECT_THAT(status.ToString(),
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
HasSubstr("[bar='\\xff']")));
}
TEST(Status, ToStringMode) {
absl::Status status(absl::StatusCode::kInternal, "fail");
status.SetPayload("foo", absl::Cord("bar"));
status.SetPayload("bar", absl::Cord("\377"));
EXPECT_EQ("INTERNAL: fail",
status.ToString(absl::StatusToStringMode::kWithNoExtraData));
EXPECT_THAT(status.ToString(absl::StatusToStringMode::kWithPayload),
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
HasSubstr("[bar='\\xff']")));
EXPECT_THAT(status.ToString(absl::StatusToStringMode::kWithEverything),
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
HasSubstr("[bar='\\xff']")));
EXPECT_THAT(status.ToString(~absl::StatusToStringMode::kWithPayload),
AllOf(HasSubstr("INTERNAL: fail"), Not(HasSubstr("[foo='bar']")),
Not(HasSubstr("[bar='\\xff']"))));
}
TEST(Status, OstreamOperator) {
absl::Status status(absl::StatusCode::kInternal, "fail");
{ std::stringstream stream;
stream << status;
EXPECT_EQ("INTERNAL: fail", stream.str());
}
status.SetPayload("foo", absl::Cord("bar"));
{ std::stringstream stream;
stream << status;
EXPECT_EQ("INTERNAL: fail [foo='bar']", stream.str());
}
status.SetPayload("bar", absl::Cord("\377"));
{ std::stringstream stream;
stream << status;
EXPECT_THAT(stream.str(),
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
HasSubstr("[bar='\\xff']")));
}
}
TEST(Status, AbslStringify) {
absl::Status status(absl::StatusCode::kInternal, "fail");
EXPECT_EQ("INTERNAL: fail", absl::StrCat(status));
EXPECT_EQ("INTERNAL: fail", absl::StrFormat("%v", status));
status.SetPayload("foo", absl::Cord("bar"));
EXPECT_EQ("INTERNAL: fail [foo='bar']", absl::StrCat(status));
status.SetPayload("bar", absl::Cord("\377"));
EXPECT_THAT(absl::StrCat(status),
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
HasSubstr("[bar='\\xff']")));
}
TEST(Status, OstreamEqStringify) {
absl::Status status(absl::StatusCode::kUnknown, "fail");
status.SetPayload("foo", absl::Cord("bar"));
std::stringstream stream;
stream << status;
EXPECT_EQ(stream.str(), absl::StrCat(status));
}
absl::Status EraseAndReturn(const absl::Status& base) {
absl::Status copy = base;
EXPECT_TRUE(copy.ErasePayload(kUrl1));
return copy;
}
TEST(Status, CopyOnWriteForErasePayload) {
{
absl::Status base(absl::StatusCode::kInvalidArgument, "fail");
base.SetPayload(kUrl1, absl::Cord(kPayload1));
EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
absl::Status copy = EraseAndReturn(base);
EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
EXPECT_FALSE(copy.GetPayload(kUrl1).has_value());
}
{
absl::Status base(absl::StatusCode::kInvalidArgument, "fail");
base.SetPayload(kUrl1, absl::Cord(kPayload1));
absl::Status copy = base;
EXPECT_TRUE(base.GetPayload(kUrl1).has_value());
EXPECT_TRUE(copy.GetPayload(kUrl1).has_value());
EXPECT_TRUE(base.ErasePayload(kUrl1));
EXPECT_FALSE(base.GetPayload(kUrl1).has_value());
EXPECT_TRUE(copy.GetPayload(kUrl1).has_value());
}
}
TEST(Status, CopyConstructor) {
{
absl::Status status;
absl::Status copy(status);
EXPECT_EQ(copy, status);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
absl::Status copy(status);
EXPECT_EQ(copy, status);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
status.SetPayload(kUrl1, absl::Cord(kPayload1));
absl::Status copy(status);
EXPECT_EQ(copy, status);
}
}
TEST(Status, CopyAssignment) {
absl::Status assignee;
{
absl::Status status;
assignee = status;
EXPECT_EQ(assignee, status);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
assignee = status;
EXPECT_EQ(assignee, status);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
status.SetPayload(kUrl1, absl::Cord(kPayload1));
assignee = status;
EXPECT_EQ(assignee, status);
}
}
TEST(Status, CopyAssignmentIsNotRef) {
const absl::Status status_orig(absl::StatusCode::kInvalidArgument, "message");
absl::Status status_copy = status_orig;
EXPECT_EQ(status_orig, status_copy);
status_copy.SetPayload(kUrl1, absl::Cord(kPayload1));
EXPECT_NE(status_orig, status_copy);
}
TEST(Status, MoveConstructor) {
{
absl::Status status;
absl::Status copy(absl::Status{});
EXPECT_EQ(copy, status);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
absl::Status copy(
absl::Status(absl::StatusCode::kInvalidArgument, "message"));
EXPECT_EQ(copy, status);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
status.SetPayload(kUrl1, absl::Cord(kPayload1));
absl::Status copy1(status);
absl::Status copy2(std::move(status));
EXPECT_EQ(copy1, copy2);
}
}
TEST(Status, MoveAssignment) {
absl::Status assignee;
{
absl::Status status;
assignee = absl::Status();
EXPECT_EQ(assignee, status);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
assignee = absl::Status(absl::StatusCode::kInvalidArgument, "message");
EXPECT_EQ(assignee, status);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
status.SetPayload(kUrl1, absl::Cord(kPayload1));
absl::Status copy(status);
assignee = std::move(status);
EXPECT_EQ(assignee, copy);
}
{
absl::Status status(absl::StatusCode::kInvalidArgument, "message");
absl::Status copy(status);
assignee = static_cast<absl::Status&&>(status);
EXPECT_EQ(assignee, copy);
}
}
TEST(Status, Update) {
absl::Status s;
s.Update(absl::OkStatus());
EXPECT_TRUE(s.ok());
const absl::Status a(absl::StatusCode::kCancelled, "message");
s.Update(a);
EXPECT_EQ(s, a);
const absl::Status b(absl::StatusCode::kInternal, "other message");
s.Update(b);
EXPECT_EQ(s, a);
s.Update(absl::OkStatus());
EXPECT_EQ(s, a);
EXPECT_FALSE(s.ok());
}
TEST(Status, Equality) {
absl::Status ok;
absl::Status no_payload = absl::CancelledError("no payload");
absl::Status one_payload = absl::InvalidArgumentError("one payload");
one_payload.SetPayload(kUrl1, absl::Cord(kPayload1));
absl::Status two_payloads = one_payload;
two_payloads.SetPayload(kUrl2, absl::Cord(kPayload2));
const std::array<absl::Status, 4> status_arr = {ok, no_payload, one_payload,
two_payloads};
for (int i = 0; i < status_arr.size(); i++) {
for (int j = 0; j < status_arr.size(); j++) {
if (i == j) {
EXPECT_TRUE(status_arr[i] == status_arr[j]);
EXPECT_FALSE(status_arr[i] != status_arr[j]);
} else {
EXPECT_TRUE(status_arr[i] != status_arr[j]);
EXPECT_FALSE(status_arr[i] == status_arr[j]);
}
}
}
}
TEST(Status, Swap) {
auto test_swap = [](const absl::Status& s1, const absl::Status& s2) {
absl::Status copy1 = s1, copy2 = s2;
swap(copy1, copy2);
EXPECT_EQ(copy1, s2);
EXPECT_EQ(copy2, s1);
};
const absl::Status ok;
const absl::Status no_payload(absl::StatusCode::kAlreadyExists, "no payload");
absl::Status with_payload(absl::StatusCode::kInternal, "with payload");
with_payload.SetPayload(kUrl1, absl::Cord(kPayload1));
test_swap(ok, no_payload);
test_swap(no_payload, ok);
test_swap(ok, with_payload);
test_swap(with_payload, ok);
test_swap(no_payload, with_payload);
test_swap(with_payload, no_payload);
}
TEST(StatusErrno, ErrnoToStatusCode) {
EXPECT_EQ(absl::ErrnoToStatusCode(0), absl::StatusCode::kOk);
// Spot-check a few errno values.
EXPECT_EQ(absl::ErrnoToStatusCode(EINVAL),
absl::StatusCode::kInvalidArgument);
EXPECT_EQ(absl::ErrnoToStatusCode(ENOENT), absl::StatusCode::kNotFound);
// We'll pick a very large number so it hopefully doesn't collide to errno.
EXPECT_EQ(absl::ErrnoToStatusCode(19980927), absl::StatusCode::kUnknown);
}
TEST(StatusErrno, ErrnoToStatus) {
absl::Status status = absl::ErrnoToStatus(ENOENT, "Cannot open 'path'");
EXPECT_EQ(status.code(), absl::StatusCode::kNotFound);
EXPECT_EQ(status.message(), "Cannot open 'path': No such file or directory");
}
} // namespace

View file

@ -0,0 +1,106 @@
// Copyright 2020 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/status/statusor.h"
#include <cstdlib>
#include <utility>
#include "absl/base/call_once.h"
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/nullability.h"
#include "absl/status/internal/statusor_internal.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
BadStatusOrAccess::BadStatusOrAccess(absl::Status status)
: status_(std::move(status)) {}
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_)) {}
absl::Nonnull<const char*> BadStatusOrAccess::what() const noexcept {
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::Nonnull<absl::Status*> status) {
const char* kMessage =
"An OK status is not a valid constructor argument to StatusOr<T>";
#ifdef NDEBUG
ABSL_INTERNAL_LOG(ERROR, kMessage);
#else
ABSL_INTERNAL_LOG(FATAL, kMessage);
#endif
// In optimized builds, we will fall back to InternalError.
*status = absl::InternalError(kMessage);
}
void Helper::Crash(const absl::Status& status) {
ABSL_INTERNAL_LOG(
FATAL,
absl::StrCat("Attempting to fetch value instead of handling error ",
status.ToString()));
}
void ThrowBadStatusOrAccess(absl::Status status) {
#ifdef ABSL_HAVE_EXCEPTIONS
throw absl::BadStatusOrAccess(std::move(status));
#else
ABSL_INTERNAL_LOG(
FATAL,
absl::StrCat("Attempting to fetch value instead of handling error ",
status.ToString()));
std::abort();
#endif
}
} // namespace internal_statusor
ABSL_NAMESPACE_END
} // namespace absl

View file

@ -0,0 +1,796 @@
// Copyright 2020 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: statusor.h
// -----------------------------------------------------------------------------
//
// An `absl::StatusOr<T>` represents a union of an `absl::Status` object
// and an object of type `T`. The `absl::StatusOr<T>` will either contain an
// object of type `T` (indicating a successful operation), or an error (of type
// `absl::Status`) explaining why such a value is not present.
//
// In general, check the success of an operation returning an
// `absl::StatusOr<T>` like you would an `absl::Status` by using the `ok()`
// member function.
//
// Example:
//
// StatusOr<Foo> result = Calculation();
// if (result.ok()) {
// result->DoSomethingCool();
// } else {
// LOG(ERROR) << result.status();
// }
#ifndef ABSL_STATUS_STATUSOR_H_
#define ABSL_STATUS_STATUSOR_H_
#include <exception>
#include <initializer_list>
#include <new>
#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/nullability.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"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/has_ostream_operator.h"
#include "absl/strings/str_format.h"
#include "absl/types/variant.h"
#include "absl/utility/utility.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
// BadStatusOrAccess
//
// This class defines the type of object to throw (if exceptions are enabled),
// when accessing the value of an `absl::StatusOr<T>` object that does not
// contain a value. This behavior is analogous to that of
// `std::bad_optional_access` in the case of accessing an invalid
// `std::optional` value.
//
// Example:
//
// try {
// absl::StatusOr<int> v = FetchInt();
// DoWork(v.value()); // Accessing value() when not "OK" may throw
// } catch (absl::BadStatusOrAccess& ex) {
// LOG(ERROR) << ex.status();
// }
class BadStatusOrAccess : public std::exception {
public:
explicit BadStatusOrAccess(absl::Status status);
~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 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.
absl::Nonnull<const char*> what() const noexcept override;
// BadStatusOrAccess::status()
//
// Returns the associated `absl::Status` of the `absl::StatusOr<T>` object's
// error.
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.
template <typename T>
#if ABSL_HAVE_CPP_ATTRIBUTE(nodiscard)
// TODO(b/176172494): ABSL_MUST_USE_RESULT should expand to the more strict
// [[nodiscard]]. For now, just use [[nodiscard]] directly when it is available.
class [[nodiscard]] StatusOr;
#else
class ABSL_MUST_USE_RESULT StatusOr;
#endif // ABSL_HAVE_CPP_ATTRIBUTE(nodiscard)
// absl::StatusOr<T>
//
// The `absl::StatusOr<T>` class template is a union of an `absl::Status` object
// and an object of type `T`. The `absl::StatusOr<T>` models an object that is
// either a usable object, or an error (of type `absl::Status`) explaining why
// such an object is not present. An `absl::StatusOr<T>` is typically the return
// value of a function which may fail.
//
// An `absl::StatusOr<T>` can never hold an "OK" status (an
// `absl::StatusCode::kOk` value); instead, the presence of an object of type
// `T` indicates success. Instead of checking for a `kOk` value, use the
// `absl::StatusOr<T>::ok()` member function. (It is for this reason, and code
// readability, that using the `ok()` function is preferred for `absl::Status`
// as well.)
//
// Example:
//
// StatusOr<Foo> result = DoBigCalculationThatCouldFail();
// if (result.ok()) {
// result->DoSomethingCool();
// } else {
// LOG(ERROR) << result.status();
// }
//
// Accessing the object held by an `absl::StatusOr<T>` should be performed via
// `operator*` or `operator->`, after a call to `ok()` confirms that the
// `absl::StatusOr<T>` holds an object of type `T`:
//
// Example:
//
// absl::StatusOr<int> i = GetCount();
// if (i.ok()) {
// updated_total += *i;
// }
//
// NOTE: using `absl::StatusOr<T>::value()` when no valid value is present will
// throw an exception if exceptions are enabled or terminate the process when
// exceptions are not enabled.
//
// Example:
//
// StatusOr<Foo> result = DoBigCalculationThatCouldFail();
// const Foo& foo = result.value(); // Crash/exception if no value present
// foo.DoSomethingCool();
//
// A `absl::StatusOr<T*>` can be constructed from a null pointer like any other
// pointer value, and the result will be that `ok()` returns `true` and
// `value()` returns `nullptr`. Checking the value of pointer in an
// `absl::StatusOr<T*>` generally requires a bit more care, to ensure both that
// a value is present and that value is not null:
//
// StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
// if (!result.ok()) {
// LOG(ERROR) << result.status();
// } else if (*result == nullptr) {
// LOG(ERROR) << "Unexpected null pointer";
// } else {
// (*result)->DoSomethingCool();
// }
//
// Example factory implementation returning StatusOr<T>:
//
// StatusOr<Foo> FooFactory::MakeFoo(int arg) {
// if (arg <= 0) {
// return absl::Status(absl::StatusCode::kInvalidArgument,
// "Arg must be positive");
// }
// return Foo(arg);
// }
template <typename T>
class StatusOr : private internal_statusor::StatusOrData<T>,
private internal_statusor::CopyCtorBase<T>,
private internal_statusor::MoveCtorBase<T>,
private internal_statusor::CopyAssignBase<T>,
private internal_statusor::MoveAssignBase<T> {
template <typename U>
friend class StatusOr;
typedef internal_statusor::StatusOrData<T> Base;
public:
// StatusOr<T>::value_type
//
// This instance data provides a generic `value_type` member for use within
// generic programming. This usage is analogous to that of
// `optional::value_type` in the case of `std::optional`.
typedef T value_type;
// Constructors
// Constructs a new `absl::StatusOr` with an `absl::StatusCode::kUnknown`
// status. This constructor is marked 'explicit' to prevent usages in return
// values such as 'return {};', under the misconception that
// `absl::StatusOr<std::vector<int>>` will be initialized with an empty
// vector, instead of an `absl::StatusCode::kUnknown` error code.
explicit StatusOr();
// `StatusOr<T>` is copy constructible if `T` is copy constructible.
StatusOr(const StatusOr&) = default;
// `StatusOr<T>` is copy assignable if `T` is copy constructible and copy
// assignable.
StatusOr& operator=(const StatusOr&) = default;
// `StatusOr<T>` is move constructible if `T` is move constructible.
StatusOr(StatusOr&&) = default;
// `StatusOr<T>` is moveAssignable if `T` is move constructible and move
// assignable.
StatusOr& operator=(StatusOr&&) = default;
// Converting Constructors
// Constructs a new `absl::StatusOr<T>` from an `absl::StatusOr<U>`, when `T`
// is constructible from `U`. To avoid ambiguity, these constructors are
// disabled if `T` is also constructible from `StatusOr<U>.`. This constructor
// is explicit if and only if the corresponding construction of `T` from `U`
// is explicit. (This constructor inherits its explicitness from the
// underlying constructor.)
template <typename U, absl::enable_if_t<
internal_statusor::IsConstructionFromStatusOrValid<
false, T, U, false, const U&>::value,
int> = 0>
StatusOr(const StatusOr<U>& other) // NOLINT
: Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
template <typename U, absl::enable_if_t<
internal_statusor::IsConstructionFromStatusOrValid<
false, T, U, true, const U&>::value,
int> = 0>
StatusOr(const StatusOr<U>& other ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT
: Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
template <typename U, absl::enable_if_t<
internal_statusor::IsConstructionFromStatusOrValid<
true, T, U, false, const U&>::value,
int> = 0>
explicit StatusOr(const StatusOr<U>& other)
: Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
template <typename U, absl::enable_if_t<
internal_statusor::IsConstructionFromStatusOrValid<
true, T, U, true, const U&>::value,
int> = 0>
explicit StatusOr(const StatusOr<U>& other ABSL_ATTRIBUTE_LIFETIME_BOUND)
: Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
template <typename U, absl::enable_if_t<
internal_statusor::IsConstructionFromStatusOrValid<
false, T, U, false, U&&>::value,
int> = 0>
StatusOr(StatusOr<U>&& other) // NOLINT
: Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
template <typename U, absl::enable_if_t<
internal_statusor::IsConstructionFromStatusOrValid<
false, T, U, true, U&&>::value,
int> = 0>
StatusOr(StatusOr<U>&& other ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT
: Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
template <typename U, absl::enable_if_t<
internal_statusor::IsConstructionFromStatusOrValid<
true, T, U, false, U&&>::value,
int> = 0>
explicit StatusOr(StatusOr<U>&& other)
: Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
template <typename U, absl::enable_if_t<
internal_statusor::IsConstructionFromStatusOrValid<
true, T, U, true, U&&>::value,
int> = 0>
explicit StatusOr(StatusOr<U>&& other ABSL_ATTRIBUTE_LIFETIME_BOUND)
: Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
// Converting Assignment Operators
// Creates an `absl::StatusOr<T>` through assignment from an
// `absl::StatusOr<U>` when:
//
// * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` are OK by assigning
// `U` to `T` directly.
// * `absl::StatusOr<T>` is OK and `absl::StatusOr<U>` contains an error
// code by destroying `absl::StatusOr<T>`'s value and assigning from
// `absl::StatusOr<U>'
// * `absl::StatusOr<T>` contains an error code and `absl::StatusOr<U>` is
// OK by directly initializing `T` from `U`.
// * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` contain an error
// code by assigning the `Status` in `absl::StatusOr<U>` to
// `absl::StatusOr<T>`
//
// These overloads only apply if `absl::StatusOr<T>` is constructible and
// assignable from `absl::StatusOr<U>` and `StatusOr<T>` cannot be directly
// assigned from `StatusOr<U>`.
template <typename U,
absl::enable_if_t<internal_statusor::IsStatusOrAssignmentValid<
T, const U&, false>::value,
int> = 0>
StatusOr& operator=(const StatusOr<U>& other) {
this->Assign(other);
return *this;
}
template <typename U,
absl::enable_if_t<internal_statusor::IsStatusOrAssignmentValid<
T, const U&, true>::value,
int> = 0>
StatusOr& operator=(const StatusOr<U>& other ABSL_ATTRIBUTE_LIFETIME_BOUND) {
this->Assign(other);
return *this;
}
template <typename U,
absl::enable_if_t<internal_statusor::IsStatusOrAssignmentValid<
T, U&&, false>::value,
int> = 0>
StatusOr& operator=(StatusOr<U>&& other) {
this->Assign(std::move(other));
return *this;
}
template <typename U,
absl::enable_if_t<internal_statusor::IsStatusOrAssignmentValid<
T, U&&, true>::value,
int> = 0>
StatusOr& operator=(StatusOr<U>&& other ABSL_ATTRIBUTE_LIFETIME_BOUND) {
this->Assign(std::move(other));
return *this;
}
// Constructs a new `absl::StatusOr<T>` with a non-ok status. After calling
// this constructor, `this->ok()` will be `false` and calls to `value()` will
// crash, or produce an exception if exceptions are enabled.
//
// The constructor also takes any type `U` that is convertible to
// `absl::Status`. This constructor is explicit if an only if `U` is not of
// type `absl::Status` and the conversion from `U` to `Status` is explicit.
//
// 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.
template <typename U = absl::Status,
absl::enable_if_t<internal_statusor::IsConstructionFromStatusValid<
false, T, U>::value,
int> = 0>
StatusOr(U&& v) : Base(std::forward<U>(v)) {}
template <typename U = absl::Status,
absl::enable_if_t<internal_statusor::IsConstructionFromStatusValid<
true, T, U>::value,
int> = 0>
explicit StatusOr(U&& v) : Base(std::forward<U>(v)) {}
template <typename U = absl::Status,
absl::enable_if_t<internal_statusor::IsConstructionFromStatusValid<
false, T, U>::value,
int> = 0>
StatusOr& operator=(U&& v) {
this->AssignStatus(std::forward<U>(v));
return *this;
}
// Perfect-forwarding value assignment operator.
// If `*this` contains a `T` value before the call, the contained value is
// assigned from `std::forward<U>(v)`; Otherwise, it is directly-initialized
// from `std::forward<U>(v)`.
// This function does not participate in overload unless:
// 1. `std::is_constructible_v<T, U>` is true,
// 2. `std::is_assignable_v<T&, U>` is true.
// 3. `std::is_same_v<StatusOr<T>, std::remove_cvref_t<U>>` is false.
// 4. Assigning `U` to `T` is not ambiguous:
// If `U` is `StatusOr<V>` and `T` is constructible and assignable from
// both `StatusOr<V>` and `V`, the assignment is considered bug-prone and
// ambiguous thus will fail to compile. For example:
// StatusOr<bool> s1 = true; // s1.ok() && *s1 == true
// StatusOr<bool> s2 = false; // s2.ok() && *s2 == false
// s1 = s2; // ambiguous, `s1 = *s2` or `s1 = bool(s2)`?
template <typename U = T,
typename std::enable_if<
internal_statusor::IsAssignmentValid<T, U, false>::value,
int>::type = 0>
StatusOr& operator=(U&& v) {
this->Assign(std::forward<U>(v));
return *this;
}
template <typename U = T,
typename std::enable_if<
internal_statusor::IsAssignmentValid<T, U, true>::value,
int>::type = 0>
StatusOr& operator=(U&& v ABSL_ATTRIBUTE_LIFETIME_BOUND) {
this->Assign(std::forward<U>(v));
return *this;
}
// Constructs the inner value `T` in-place using the provided args, using the
// `T(args...)` constructor.
template <typename... Args>
explicit StatusOr(absl::in_place_t, Args&&... args);
template <typename U, typename... Args>
explicit StatusOr(absl::in_place_t, std::initializer_list<U> ilist,
Args&&... args);
// Constructs the inner value `T` in-place using the provided args, using the
// `T(U)` (direct-initialization) constructor. This constructor is only valid
// if `T` can be constructed from a `U`. Can accept move or copy constructors.
//
// This constructor is explicit if `U` is not convertible to `T`. To avoid
// ambiguity, this constructor is disabled if `U` is a `StatusOr<J>`, where
// `J` is convertible to `T`.
template <typename U = T,
absl::enable_if_t<internal_statusor::IsConstructionValid<
false, T, U, false>::value,
int> = 0>
StatusOr(U&& u) // NOLINT
: StatusOr(absl::in_place, std::forward<U>(u)) {}
template <typename U = T,
absl::enable_if_t<internal_statusor::IsConstructionValid<
false, T, U, true>::value,
int> = 0>
StatusOr(U&& u ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT
: StatusOr(absl::in_place, std::forward<U>(u)) {}
template <typename U = T,
absl::enable_if_t<internal_statusor::IsConstructionValid<
true, T, U, false>::value,
int> = 0>
explicit StatusOr(U&& u) // NOLINT
: StatusOr(absl::in_place, std::forward<U>(u)) {}
template <typename U = T,
absl::enable_if_t<
internal_statusor::IsConstructionValid<true, T, U, true>::value,
int> = 0>
explicit StatusOr(U&& u ABSL_ATTRIBUTE_LIFETIME_BOUND) // NOLINT
: StatusOr(absl::in_place, std::forward<U>(u)) {}
// StatusOr<T>::ok()
//
// Returns whether or not this `absl::StatusOr<T>` holds a `T` value. This
// member function is analogous to `absl::Status::ok()` and should be used
// similarly to check the status of return values.
//
// Example:
//
// StatusOr<Foo> result = DoBigCalculationThatCouldFail();
// if (result.ok()) {
// // Handle result
// else {
// // Handle error
// }
ABSL_MUST_USE_RESULT bool ok() const { return this->status_.ok(); }
// StatusOr<T>::status()
//
// 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&;
Status status() &&;
// StatusOr<T>::value()
//
// Returns a reference to the held value if `this->ok()`. Otherwise, throws
// `absl::BadStatusOrAccess` if exceptions are enabled, or is guaranteed to
// terminate the process if exceptions are disabled.
//
// If you have already checked the status using `this->ok()`, you probably
// want to use `operator*()` or `operator->()` to access the value instead of
// `value`.
//
// Note: for value types that are cheap to copy, prefer simple code:
//
// T value = statusor.value();
//
// Otherwise, if the value type is expensive to copy, but can be left
// in the StatusOr, simply assign to a reference:
//
// T& value = statusor.value(); // or `const T&`
//
// Otherwise, if the value type supports an efficient move, it can be
// used as follows:
//
// T value = std::move(statusor).value();
//
// The `std::move` on statusor instead of on the whole expression enables
// warnings about possible uses of the statusor object after the move.
const T& value() const& ABSL_ATTRIBUTE_LIFETIME_BOUND;
T& value() & ABSL_ATTRIBUTE_LIFETIME_BOUND;
const T&& value() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND;
T&& value() && ABSL_ATTRIBUTE_LIFETIME_BOUND;
// StatusOr<T>:: operator*()
//
// Returns a reference to the current value.
//
// REQUIRES: `this->ok() == true`, otherwise the behavior is undefined.
//
// Use `this->ok()` to verify that there is a current value within the
// `absl::StatusOr<T>`. Alternatively, see the `value()` member function for a
// similar API that guarantees crashing or throwing an exception if there is
// no current value.
const T& operator*() const& ABSL_ATTRIBUTE_LIFETIME_BOUND;
T& operator*() & ABSL_ATTRIBUTE_LIFETIME_BOUND;
const T&& operator*() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND;
T&& operator*() && ABSL_ATTRIBUTE_LIFETIME_BOUND;
// StatusOr<T>::operator->()
//
// Returns a pointer to the current value.
//
// REQUIRES: `this->ok() == true`, otherwise the behavior is undefined.
//
// Use `this->ok()` to verify that there is a current value.
const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND;
// StatusOr<T>::value_or()
//
// Returns the current value if `this->ok() == true`. Otherwise constructs a
// value using the provided `default_value`.
//
// Unlike `value`, this function returns by value, copying the current value
// if necessary. If the value type supports an efficient move, it can be used
// as follows:
//
// T value = std::move(statusor).value_or(def);
//
// Unlike with `value`, calling `std::move()` on the result of `value_or` will
// still trigger a copy.
template <typename U>
T value_or(U&& default_value) const&;
template <typename U>
T value_or(U&& default_value) &&;
// StatusOr<T>::IgnoreError()
//
// Ignores any errors. This method does nothing except potentially suppress
// complaints from any tools that are checking that errors are not dropped on
// the floor.
void IgnoreError() const;
// StatusOr<T>::emplace()
//
// 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) ABSL_ATTRIBUTE_LIFETIME_BOUND {
if (ok()) {
this->Clear();
this->MakeValue(std::forward<Args>(args)...);
} else {
this->MakeValue(std::forward<Args>(args)...);
this->status_ = absl::OkStatus();
}
return this->data_;
}
template <
typename U, typename... Args,
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) ABSL_ATTRIBUTE_LIFETIME_BOUND {
if (ok()) {
this->Clear();
this->MakeValue(ilist, std::forward<Args>(args)...);
} else {
this->MakeValue(ilist, std::forward<Args>(args)...);
this->status_ = absl::OkStatus();
}
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>
void Assign(const absl::StatusOr<U>& other);
template <typename U>
void Assign(absl::StatusOr<U>&& other);
};
// operator==()
//
// This operator checks the equality of two `absl::StatusOr<T>` objects.
template <typename T>
bool operator==(const StatusOr<T>& lhs, const StatusOr<T>& rhs) {
if (lhs.ok() && rhs.ok()) return *lhs == *rhs;
return lhs.status() == rhs.status();
}
// operator!=()
//
// This operator checks the inequality of two `absl::StatusOr<T>` objects.
template <typename T>
bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) {
return !(lhs == rhs);
}
// Prints the `value` or the status in brackets to `os`.
//
// Requires `T` supports `operator<<`. Do not rely on the output format which
// may change without notice.
template <typename T, typename std::enable_if<
absl::HasOstreamOperator<T>::value, int>::type = 0>
std::ostream& operator<<(std::ostream& os, const StatusOr<T>& status_or) {
if (status_or.ok()) {
os << status_or.value();
} else {
os << internal_statusor::StringifyRandom::OpenBrackets()
<< status_or.status()
<< internal_statusor::StringifyRandom::CloseBrackets();
}
return os;
}
// As above, but supports `StrCat`, `StrFormat`, etc.
//
// Requires `T` has `AbslStringify`. Do not rely on the output format which
// may change without notice.
template <
typename Sink, typename T,
typename std::enable_if<absl::HasAbslStringify<T>::value, int>::type = 0>
void AbslStringify(Sink& sink, const StatusOr<T>& status_or) {
if (status_or.ok()) {
absl::Format(&sink, "%v", status_or.value());
} else {
absl::Format(&sink, "%s%v%s",
internal_statusor::StringifyRandom::OpenBrackets(),
status_or.status(),
internal_statusor::StringifyRandom::CloseBrackets());
}
}
//------------------------------------------------------------------------------
// Implementation details for StatusOr<T>
//------------------------------------------------------------------------------
// TODO(sbenza): avoid the string here completely.
template <typename T>
StatusOr<T>::StatusOr() : Base(Status(absl::StatusCode::kUnknown, "")) {}
template <typename T>
template <typename U>
inline void StatusOr<T>::Assign(const StatusOr<U>& other) {
if (other.ok()) {
this->Assign(*other);
} else {
this->AssignStatus(other.status());
}
}
template <typename T>
template <typename U>
inline void StatusOr<T>::Assign(StatusOr<U>&& other) {
if (other.ok()) {
this->Assign(*std::move(other));
} else {
this->AssignStatus(std::move(other).status());
}
}
template <typename T>
template <typename... Args>
StatusOr<T>::StatusOr(absl::in_place_t, Args&&... args)
: Base(absl::in_place, std::forward<Args>(args)...) {}
template <typename T>
template <typename U, typename... Args>
StatusOr<T>::StatusOr(absl::in_place_t, std::initializer_list<U> ilist,
Args&&... args)
: Base(absl::in_place, ilist, std::forward<Args>(args)...) {}
template <typename T>
const Status& StatusOr<T>::status() const& {
return this->status_;
}
template <typename T>
Status StatusOr<T>::status() && {
return ok() ? OkStatus() : std::move(this->status_);
}
template <typename T>
const T& StatusOr<T>::value() const& {
if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_);
return this->data_;
}
template <typename T>
T& StatusOr<T>::value() & {
if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_);
return this->data_;
}
template <typename T>
const T&& StatusOr<T>::value() const&& {
if (!this->ok()) {
internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_));
}
return std::move(this->data_);
}
template <typename T>
T&& StatusOr<T>::value() && {
if (!this->ok()) {
internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_));
}
return std::move(this->data_);
}
template <typename T>
const T& StatusOr<T>::operator*() const& {
this->EnsureOk();
return this->data_;
}
template <typename T>
T& StatusOr<T>::operator*() & {
this->EnsureOk();
return this->data_;
}
template <typename T>
const T&& StatusOr<T>::operator*() const&& {
this->EnsureOk();
return std::move(this->data_);
}
template <typename T>
T&& StatusOr<T>::operator*() && {
this->EnsureOk();
return std::move(this->data_);
}
template <typename T>
absl::Nonnull<const T*> StatusOr<T>::operator->() const {
this->EnsureOk();
return &this->data_;
}
template <typename T>
absl::Nonnull<T*> StatusOr<T>::operator->() {
this->EnsureOk();
return &this->data_;
}
template <typename T>
template <typename U>
T StatusOr<T>::value_or(U&& default_value) const& {
if (ok()) {
return this->data_;
}
return std::forward<U>(default_value);
}
template <typename T>
template <typename U>
T StatusOr<T>::value_or(U&& default_value) && {
if (ok()) {
return std::move(this->data_);
}
return std::forward<U>(default_value);
}
template <typename T>
void StatusOr<T>::IgnoreError() const {
// no-op
}
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STATUS_STATUSOR_H_

File diff suppressed because it is too large Load diff