Bring sapi::StatusOr<> up to date with internal Abseil changes

Note: This intentionally omits perfect-forwarding value assignments. This
avoids overly complex template expressions. The regular assignments are still
efficient.
PiperOrigin-RevId: 304159053
Change-Id: I3460f46ca5779a0619cf90ae22625de8fad7669c
This commit is contained in:
Christian Blichmann 2020-04-01 04:40:18 -07:00 committed by Copybara-Service
parent 2fa0bf85f4
commit 2b2e7ac498
2 changed files with 172 additions and 21 deletions

View File

@ -19,6 +19,9 @@
#ifndef THIRD_PARTY_SAPI_UTIL_STATUSOR_H_ #ifndef THIRD_PARTY_SAPI_UTIL_STATUSOR_H_
#define THIRD_PARTY_SAPI_UTIL_STATUSOR_H_ #define THIRD_PARTY_SAPI_UTIL_STATUSOR_H_
#include <initializer_list>
#include <utility>
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/base/log_severity.h" #include "absl/base/log_severity.h"
@ -29,20 +32,15 @@
namespace sapi { namespace sapi {
template <typename T> template <typename T>
class StatusOr { class ABSL_MUST_USE_RESULT StatusOr {
template <typename U>
friend class StatusOr;
public: public:
using element_type = T;
explicit StatusOr() : variant_(absl::UnknownError("")) {} explicit StatusOr() : variant_(absl::UnknownError("")) {}
StatusOr(const absl::Status& status) : variant_(status) { EnsureNotOk(); }
StatusOr& operator=(const absl::Status& status) {
variant_ = status;
EnsureNotOk();
}
StatusOr(const T& value) : variant_{value} {}
StatusOr(T&& value) : variant_{std::move(value)} {}
StatusOr(const StatusOr&) = default; StatusOr(const StatusOr&) = default;
StatusOr& operator=(const StatusOr&) = default; StatusOr& operator=(const StatusOr&) = default;
@ -50,27 +48,84 @@ class StatusOr {
StatusOr& operator=(StatusOr&&) = default; StatusOr& operator=(StatusOr&&) = default;
template <typename U> template <typename U>
StatusOr(const StatusOr<U>& other) { StatusOr(const StatusOr<U>& other) : StatusOr(other) {}
*this = other;
} template <typename U>
StatusOr(StatusOr<U>&& other) : StatusOr(other) {}
template <typename U> template <typename U>
StatusOr& operator=(const StatusOr<U>& other) { StatusOr& operator=(const StatusOr<U>& other) {
if (other.ok()) { variant_ = other.ok() ? other.value() : other.status();
variant_ = other.ValueOrDie();
} else {
variant_ = other.status();
}
return *this; return *this;
} }
template <typename U>
StatusOr& operator=(StatusOr<U>&& other) {
variant_ =
other.ok() ? std::move(other).value() : std::move(other).status();
return *this;
}
StatusOr(const T& value) : variant_(value) {}
StatusOr(const absl::Status& status) : variant_(status) { EnsureNotOk(); }
// Not implemented:
// template <typename U = T>
// StatusOr& operator=(U&& value);
StatusOr(T&& value) : variant_(std::move(value)) {}
StatusOr(absl::Status&& value) : variant_(std::move(value)) {}
StatusOr& operator=(absl::Status&& status) {
variant_ = std::move(status);
EnsureNotOk();
}
template <typename... Args>
explicit StatusOr(absl::in_place_t, Args&&... args)
: StatusOr(T(std::forward<Args>(args)...)) {}
template <typename U, typename... Args>
explicit StatusOr(absl::in_place_t, std::initializer_list<U> ilist,
Args&&... args)
: StatusOr(ilist, U(std::forward<Args>(args)...)) {}
explicit operator bool() const { return ok(); } explicit operator bool() const { return ok(); }
ABSL_MUST_USE_RESULT bool ok() const { ABSL_MUST_USE_RESULT bool ok() const {
return absl::holds_alternative<T>(variant_); return absl::holds_alternative<T>(variant_);
} }
absl::Status status() const { const absl::Status& status() const& {
return ok() ? absl::OkStatus() : absl::get<absl::Status>(variant_); static const auto* ok_status = new absl::Status();
return ok() ? *ok_status : absl::get<absl::Status>(variant_);
}
absl::Status status() && {
return ok() ? absl::OkStatus()
: std::move(absl::get<absl::Status>(variant_));
}
const T& value() const& {
EnsureOk();
return absl::get<T>(variant_);
}
T& value() & {
EnsureOk();
return absl::get<T>(variant_);
}
const T&& value() const&& {
EnsureOk();
return std::move(absl::get<T>(variant_));
}
T&& value() && {
EnsureOk();
return std::move(absl::get<T>(variant_));
} }
const T& ValueOrDie() const& { const T& ValueOrDie() const& {
@ -88,6 +143,65 @@ class StatusOr {
return std::move(absl::get<T>(variant_)); return std::move(absl::get<T>(variant_));
} }
const T& operator*() const& {
EnsureOk();
return absl::get<T>(variant_);
}
T& operator*() & {
EnsureOk();
return absl::get<T>(variant_);
}
const T&& operator*() const&& {
EnsureOk();
return std::move(absl::get<T>(variant_));
}
T&& operator*() && {
EnsureOk();
return std::move(absl::get<T>(variant_));
}
const T* operator->() const {
EnsureOk();
return &absl::get<T>(variant_);
}
T* operator->() {
EnsureOk();
return &absl::get<T>(variant_);
}
template <typename U>
T value_or(U&& default_value) const& {
if (ok()) {
return absl::get<T>(variant_);
}
return std::forward<U>(default_value);
}
template <typename U>
T value_or(U&& default_value) && {
if (ok()) {
return std::move(absl::get<T>(variant_));
}
return std::forward<U>(default_value);
}
void IgnoreError() const { /* no-op */
}
template <typename... Args>
T& emplace(Args&&... args) {
return variant_.template emplace<T>(std::forward<Args>(args)...);
}
template <typename U, typename... Args>
T& emplace(std::initializer_list<U> ilist, Args&&... args) {
return variant_.template emplace<T>(ilist, std::forward<Args>(args)...);
}
private: private:
void EnsureOk() const { void EnsureOk() const {
if (!ok()) { if (!ok()) {

View File

@ -28,6 +28,7 @@
using ::testing::Eq; using ::testing::Eq;
using ::testing::IsFalse; using ::testing::IsFalse;
using ::testing::Not; using ::testing::Not;
using ::testing::Pointee;
namespace sapi { namespace sapi {
namespace { namespace {
@ -366,5 +367,41 @@ TEST(StatusOrTest, ValueOrDieMovedValue) {
EXPECT_THAT(*moved_value, Eq(kStringElement)); EXPECT_THAT(*moved_value, Eq(kStringElement));
} }
TEST(StatusOrTest, MapToStatusOrUniquePtr) {
// A reduced version of a problematic type found in the wild. All of the
// operations below should compile.
using MapType = std::map<std::string, StatusOr<std::unique_ptr<int>>>;
MapType a;
// Move-construction
MapType b(std::move(a));
// Move-assignment
a = std::move(b);
}
TEST(StatusOrTest, ValueOrOk) {
const StatusOr<int> status_or = 0;
EXPECT_EQ(status_or.value_or(-1), 0);
}
TEST(StatusOrTest, ValueOrDefault) {
const StatusOr<int> status_or = absl::CancelledError();
EXPECT_EQ(status_or.value_or(-1), -1);
}
TEST(StatusOrTest, MoveOnlyValueOrOk) {
EXPECT_THAT(StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(0))
.value_or(absl::make_unique<int>(-1)),
Pointee(0));
}
TEST(StatusOr, MoveOnlyValueOrDefault) {
EXPECT_THAT(StatusOr<std::unique_ptr<int>>(absl::CancelledError())
.value_or(absl::make_unique<int>(-1)),
Pointee(-1));
}
} // namespace } // namespace
} // namespace sapi } // namespace sapi