Commit 0e9921b7 by Abseil Team Committed by Mark Barolak

Export of internal Abseil changes

--
23500704dd7c2642fad49f88c07ce41ebaab12e4 by Abseil Team <absl-team@google.com>:

Change fetch_add() to store(1 + load())

As there is only one concurrent writer to the Info struct, we can avoid using
the more expensive fetch_add() function.

PiperOrigin-RevId: 329523785

--
79e5018dba2e117ad89b76367165a604b3f24045 by Abseil Team <absl-team@google.com>:

Record rehash count in hashtablez

This will help identify which containers could benefit from a reserve call.

PiperOrigin-RevId: 329510552

--
e327e54b805d67556f934fa7f7dc2d4e72fa066a by Abseil Team <absl-team@google.com>:

Fix -Wsign-compare issues.

These lines could theoretically have overflowed in cases of very large stack
traces etc. Very unlikely, but this was causing warnings when built with
-Wsign-compare, which we intend to enable with Chromium.

In none of these three cases is it trivial to switch to a range-based for loop.

PiperOrigin-RevId: 329415195

--
08aca2fc75e8b3ad1201849987b64148fe48f283 by Xiaoyi Zhang <zhangxy@google.com>:

Release absl::StatusOr.

PiperOrigin-RevId: 329353348

--
bf4d2a7f8b089e2adf14d32b0e39de0a981005c3 by Xiaoyi Zhang <zhangxy@google.com>:

Internal change

PiperOrigin-RevId: 329337031

--
42fa7d2fb993bbfc344954227cf1eeb801eca065 by Abseil Team <absl-team@google.com>:

Internal change

PiperOrigin-RevId: 329099807
GitOrigin-RevId: 23500704dd7c2642fad49f88c07ce41ebaab12e4
Change-Id: I6713e4ca3bb0ab2ced5e487827ae036ab8ac61f1
parent a4cbb5f6
......@@ -175,8 +175,11 @@ set(ABSL_INTERNAL_DLL_FILES
"random/uniform_real_distribution.h"
"random/zipf_distribution.h"
"status/internal/status_internal.h"
"status/internal/statusor_internal.h"
"status/status.h"
"status/status.cc"
"status/statusor.h"
"status/statusor.cc"
"status/status_payload_printer.h"
"status/status_payload_printer.cc"
"strings/ascii.cc"
......
......@@ -67,6 +67,7 @@ void HashtablezInfo::PrepareForSampling() {
capacity.store(0, std::memory_order_relaxed);
size.store(0, std::memory_order_relaxed);
num_erases.store(0, std::memory_order_relaxed);
num_rehashes.store(0, std::memory_order_relaxed);
max_probe_length.store(0, std::memory_order_relaxed);
total_probe_length.store(0, std::memory_order_relaxed);
hashes_bitwise_or.store(0, std::memory_order_relaxed);
......
......@@ -73,6 +73,7 @@ struct HashtablezInfo {
std::atomic<size_t> capacity;
std::atomic<size_t> size;
std::atomic<size_t> num_erases;
std::atomic<size_t> num_rehashes;
std::atomic<size_t> max_probe_length;
std::atomic<size_t> total_probe_length;
std::atomic<size_t> hashes_bitwise_or;
......@@ -105,6 +106,11 @@ inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
#endif
info->total_probe_length.store(total_probe_length, std::memory_order_relaxed);
info->num_erases.store(0, std::memory_order_relaxed);
// There is only one concurrent writer, so `load` then `store` is sufficient
// instead of using `fetch_add`.
info->num_rehashes.store(
1 + info->num_rehashes.load(std::memory_order_relaxed),
std::memory_order_relaxed);
}
inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
......@@ -113,7 +119,8 @@ inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
info->capacity.store(capacity, std::memory_order_relaxed);
if (size == 0) {
// This is a clear, reset the total/num_erases too.
RecordRehashSlow(info, 0);
info->total_probe_length.store(0, std::memory_order_relaxed);
info->num_erases.store(0, std::memory_order_relaxed);
}
}
......@@ -122,7 +129,11 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash,
inline void RecordEraseSlow(HashtablezInfo* info) {
info->size.fetch_sub(1, std::memory_order_relaxed);
info->num_erases.fetch_add(1, std::memory_order_relaxed);
// There is only one concurrent writer, so `load` then `store` is sufficient
// instead of using `fetch_add`.
info->num_erases.store(
1 + info->num_erases.load(std::memory_order_relaxed),
std::memory_order_relaxed);
}
HashtablezInfo* SampleSlow(int64_t* next_sample);
......
......@@ -76,6 +76,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
EXPECT_EQ(info.capacity.load(), 0);
EXPECT_EQ(info.size.load(), 0);
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.num_rehashes.load(), 0);
EXPECT_EQ(info.max_probe_length.load(), 0);
EXPECT_EQ(info.total_probe_length.load(), 0);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
......@@ -95,6 +96,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) {
EXPECT_EQ(info.capacity.load(), 0);
EXPECT_EQ(info.size.load(), 0);
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.num_rehashes.load(), 0);
EXPECT_EQ(info.max_probe_length.load(), 0);
EXPECT_EQ(info.total_probe_length.load(), 0);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
......@@ -167,6 +169,7 @@ TEST(HashtablezInfoTest, RecordRehash) {
EXPECT_EQ(info.size.load(), 2);
EXPECT_EQ(info.total_probe_length.load(), 3);
EXPECT_EQ(info.num_erases.load(), 0);
EXPECT_EQ(info.num_rehashes.load(), 1);
}
#if defined(ABSL_HASHTABLEZ_SAMPLE)
......
......@@ -65,3 +65,39 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "statusor",
srcs = [
"internal/statusor_internal.h",
"statusor.cc",
],
hdrs = [
"statusor.h",
],
copts = ABSL_DEFAULT_COPTS,
deps = [
":status",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"//absl/meta:type_traits",
"//absl/strings",
"//absl/types:variant",
"//absl/utility",
],
)
cc_test(
name = "statusor_test",
size = "small",
srcs = ["statusor_test.cc"],
deps = [
":status",
":statusor",
"//absl/base",
"//absl/memory",
"//absl/types:any",
"//absl/utility",
"@com_google_googletest//:gtest_main",
],
)
......@@ -52,3 +52,36 @@ absl_cc_test(
absl::strings
gmock_main
)
absl_cc_library(
NAME
statusor
HDRS
"statusor.h"
SRCS
"statusor.cc"
"internal/statusor_internal.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::core_headers
absl::raw_logging_internal
absl::type_traits
absl::strings
absl::utility
absl::variant
PUBLIC
)
absl_cc_test(
NAME
statusor_test
SRCS
"statusor_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::status
absl::statusor
gmock_main
)
// 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 <type_traits>
#include <utility>
#include "absl/meta/type_traits.h"
#include "absl/status/status.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_cv_t<absl::remove_reference_t<U>>,
U>::value,
std::false_type,
IsDirectInitializationAmbiguous<
T, absl::remove_cv_t<absl::remove_reference_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_cv_t<absl::remove_reference_t<U>>>,
absl::negation<absl::disjunction<
std::is_same<absl::StatusOr<T>,
absl::remove_cv_t<absl::remove_reference_t<U>>>,
std::is_same<absl::Status,
absl::remove_cv_t<absl::remove_reference_t<U>>>,
std::is_same<absl::in_place_t,
absl::remove_cv_t<absl::remove_reference_t<U>>>,
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_cv_t<absl::remove_reference_t<U>>,
U>::value,
std::false_type,
IsForwardingAssignmentAmbiguous<
T, absl::remove_cv_t<absl::remove_reference_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_cv_t<absl::remove_reference_t<U>>>,
absl::negation<absl::disjunction<
std::is_same<absl::StatusOr<T>,
absl::remove_cv_t<absl::remove_reference_t<U>>>,
std::is_same<absl::Status,
absl::remove_cv_t<absl::remove_reference_t<U>>>,
std::is_same<absl::in_place_t,
absl::remove_cv_t<absl::remove_reference_t<U>>>,
IsForwardingAssignmentAmbiguous<T, U>>>>;
class Helper {
public:
// Move type-agnostic error handling to the .cc.
static void HandleInvalidStatusCtorArg(Status*);
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>
void PlacementNew(void* p, Args&&... args) {
#if defined(__GNUC__) && !defined(__clang__)
// Teach gcc that 'p' cannot be null, fixing code size issues.
if (p == nullptr) __builtin_unreachable();
#endif
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_(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;
};
ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status);
} // namespace internal_statusor
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
......@@ -78,7 +78,7 @@ static int FindPayloadIndexByUrl(const Payloads* payloads,
absl::string_view type_url) {
if (payloads == nullptr) return -1;
for (int i = 0; i < payloads->size(); ++i) {
for (size_t i = 0; i < payloads->size(); ++i) {
if ((*payloads)[i].type_url == type_url) return i;
}
......@@ -167,7 +167,7 @@ void Status::ForEachPayload(
bool in_reverse =
payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
for (int index = 0; index < payloads->size(); ++index) {
for (size_t index = 0; index < payloads->size(); ++index) {
const auto& elem =
(*payloads)[in_reverse ? payloads->size() - 1 - index : index];
......
// 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/internal/raw_logging.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() = default;
const char* BadStatusOrAccess::what() const noexcept {
return "Bad StatusOr access";
}
const absl::Status& BadStatusOrAccess::status() const { return status_; }
namespace internal_statusor {
void Helper::HandleInvalidStatusCtorArg(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
// 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.
//
// StatusOr<T> is the union of a Status object and a T
// object. StatusOr models the concept of an object that is either a
// usable value, or an error Status explaining why such a value is
// not present. To this end, StatusOr<T> does not allow its Status
// value to be absl::OkStatus().
//
// The primary use-case for StatusOr<T> is as the return value of a
// function which may fail.
//
// Example usage of a StatusOr<T>:
//
// StatusOr<Foo> result = DoBigCalculationThatCouldFail();
// if (result.ok()) {
// result->DoSomethingCool();
// } else {
// LOG(ERROR) << result.status();
// }
//
// Example that is guaranteed to crash if the result holds no value:
//
// StatusOr<Foo> result = DoBigCalculationThatCouldFail();
// const Foo& foo = result.value();
// foo.DoSomethingCool();
//
// Example usage of a StatusOr<std::unique_ptr<T>>:
//
// StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
// if (!result.ok()) { // Don't omit .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);
// }
//
// NULL POINTERS
//
// Historically StatusOr<T*> treated null pointers specially. This is no longer
// true -- a 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 null.
#ifndef ABSL_STATUS_STATUSOR_H_
#define ABSL_STATUS_STATUSOR_H_
#include <exception>
#include <initializer_list>
#include <new>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/meta/type_traits.h"
#include "absl/status/internal/statusor_internal.h"
#include "absl/status/status.h"
#include "absl/types/variant.h"
#include "absl/utility/utility.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
class BadStatusOrAccess : public std::exception {
public:
explicit BadStatusOrAccess(absl::Status status);
~BadStatusOrAccess() override;
const char* what() const noexcept override;
const absl::Status& status() const;
private:
absl::Status status_;
};
// Returned StatusOr objects may not be ignored.
template <typename T>
class ABSL_MUST_USE_RESULT StatusOr;
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:
typedef T value_type;
// Constructs a new StatusOr with Status::UNKNOWN status. This is marked
// 'explicit' to try to catch cases like 'return {};', where people think
// absl::StatusOr<std::vector<int>> will be initialized with an empty vector,
// instead of a Status::UNKNOWN status.
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 from StatusOr<U>, when T is constructible from U.
// To avoid ambiguity, they are disabled if T is also constructible from
// StatusOr<U>. Explicit iff the corresponding construction of T from U is
// explicit.
template <
typename U,
absl::enable_if_t<
absl::conjunction<
absl::negation<std::is_same<T, U>>,
std::is_constructible<T, const U&>,
std::is_convertible<const U&, T>,
absl::negation<
internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
T, 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<
absl::conjunction<
absl::negation<std::is_same<T, U>>,
std::is_constructible<T, const U&>,
absl::negation<std::is_convertible<const U&, T>>,
absl::negation<
internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
T, 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<
absl::conjunction<
absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
std::is_convertible<U&&, T>,
absl::negation<
internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
T, U>>>::value,
int> = 0>
StatusOr(StatusOr<U>&& other) // NOLINT
: Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
template <
typename U,
absl::enable_if_t<
absl::conjunction<
absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
absl::negation<std::is_convertible<U&&, T>>,
absl::negation<
internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
T, U>>>::value,
int> = 0>
explicit StatusOr(StatusOr<U>&& other)
: Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
// Conversion copy/move assignment operator, T must be constructible and
// assignable from U. Only enable if T cannot be directly assigned from
// StatusOr<U>.
template <
typename U,
absl::enable_if_t<
absl::conjunction<
absl::negation<std::is_same<T, U>>,
std::is_constructible<T, const U&>,
std::is_assignable<T, const U&>,
absl::negation<
internal_statusor::
IsConstructibleOrConvertibleOrAssignableFromStatusOr<
T, U>>>::value,
int> = 0>
StatusOr& operator=(const StatusOr<U>& other) {
this->Assign(other);
return *this;
}
template <
typename U,
absl::enable_if_t<
absl::conjunction<
absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
std::is_assignable<T, U&&>,
absl::negation<
internal_statusor::
IsConstructibleOrConvertibleOrAssignableFromStatusOr<
T, U>>>::value,
int> = 0>
StatusOr& operator=(StatusOr<U>&& other) {
this->Assign(std::move(other));
return *this;
}
// Constructs a new StatusOr with a non-ok status. After calling this
// constructor, this->ok() will be false and calls to value() will CHECK-fail.
// The constructor also takes any type `U` that is convertible to `Status`.
//
// NOTE: Not explicit - we want to use StatusOr<T> as a return
// value, so it is convenient and sensible to be able to do
// `return Status()` or `return ConvertibleToStatus()` when the return type
// is `StatusOr<T>`.
//
// 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<
absl::conjunction<
std::is_convertible<U&&, absl::Status>,
std::is_constructible<absl::Status, U&&>,
absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
absl::negation<std::is_same<absl::decay_t<U>, T>>,
absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
T, U&&>>>::value,
int> = 0>
StatusOr(U&& v) : Base(std::forward<U>(v)) {}
template <
typename U = absl::Status,
absl::enable_if_t<
absl::conjunction<
absl::negation<std::is_convertible<U&&, absl::Status>>,
std::is_constructible<absl::Status, U&&>,
absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
absl::negation<std::is_same<absl::decay_t<U>, T>>,
absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
T, U&&>>>::value,
int> = 0>
explicit StatusOr(U&& v) : Base(std::forward<U>(v)) {}
template <
typename U = absl::Status,
absl::enable_if_t<
absl::conjunction<
std::is_convertible<U&&, absl::Status>,
std::is_constructible<absl::Status, U&&>,
absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
absl::negation<std::is_same<absl::decay_t<U>, T>>,
absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
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 = typename std::enable_if<absl::conjunction<
std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>,
absl::disjunction<
std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, T>,
absl::conjunction<
absl::negation<std::is_convertible<U&&, absl::Status>>,
absl::negation<internal_statusor::
HasConversionOperatorToStatusOr<T, U&&>>>>,
internal_statusor::IsForwardingAssignmentValid<T, U&&>>::value>::type>
StatusOr& operator=(U&& v) {
static_assert(
!absl::conjunction<
std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>,
std::is_constructible<absl::Status, U&&>,
std::is_assignable<absl::Status&, U&&>,
absl::negation<std::is_same<
T, absl::remove_cv_t<absl::remove_reference_t<U>>>>>::value,
"U can assign to both T and Status, will result in semantic change");
static_assert(
!absl::conjunction<
std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>,
internal_statusor::HasConversionOperatorToStatusOr<T, U&&>,
absl::negation<std::is_same<
T, absl::remove_cv_t<absl::remove_reference_t<U>>>>>::value,
"U can assign to T and convert to StatusOr<T>, will result in semantic "
"change");
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. Only valid if T can be
// constructed from a U. Can accept move or copy constructors. Explicit if
// U is not convertible to T. To avoid ambiguity, this is disabled if U is
// a StatusOr<J>, where J is convertible to T.
template <
typename U = T,
absl::enable_if_t<
absl::conjunction<
internal_statusor::IsDirectInitializationValid<T, U&&>,
std::is_constructible<T, U&&>, std::is_convertible<U&&, T>,
absl::disjunction<
std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
T>,
absl::conjunction<
absl::negation<std::is_convertible<U&&, absl::Status>>,
absl::negation<
internal_statusor::HasConversionOperatorToStatusOr<
T, U&&>>>>>::value,
int> = 0>
StatusOr(U&& u) // NOLINT
: StatusOr(absl::in_place, std::forward<U>(u)) {
static_assert(
!absl::conjunction<
std::is_convertible<U&&, T>, std::is_convertible<U&&, absl::Status>,
absl::negation<std::is_same<
T, absl::remove_cv_t<absl::remove_reference_t<U>>>>>::value,
"U is convertible to both T and Status, will result in semantic "
"change");
static_assert(
!absl::conjunction<
std::is_convertible<U&&, T>,
internal_statusor::HasConversionOperatorToStatusOr<T, U&&>,
absl::negation<std::is_same<
T, absl::remove_cv_t<absl::remove_reference_t<U>>>>>::value,
"U can construct T and convert to StatusOr<T>, will result in semantic "
"change");
}
template <
typename U = T,
absl::enable_if_t<
absl::conjunction<
internal_statusor::IsDirectInitializationValid<T, U&&>,
absl::disjunction<
std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
T>,
absl::conjunction<
absl::negation<std::is_constructible<absl::Status, U&&>>,
absl::negation<
internal_statusor::HasConversionOperatorToStatusOr<
T, U&&>>>>,
std::is_constructible<T, U&&>,
absl::negation<std::is_convertible<U&&, T>>>::value,
int> = 0>
explicit StatusOr(U&& u) // NOLINT
: StatusOr(absl::in_place, std::forward<U>(u)) {
static_assert(
!absl::conjunction<
std::is_constructible<T, U&&>,
std::is_constructible<absl::Status, U&&>,
absl::negation<std::is_same<
T, absl::remove_cv_t<absl::remove_reference_t<U>>>>>::value,
"U can construct both T and Status, will result in semantic "
"change");
static_assert(
!absl::conjunction<
std::is_constructible<T, U&&>,
internal_statusor::HasConversionOperatorToStatusOr<T, U&&>,
absl::negation<std::is_same<
T, absl::remove_cv_t<absl::remove_reference_t<U>>>>>::value,
"U can construct T and convert to StatusOr<T>, will result in semantic "
"change");
}
// Returns this->status().ok()
ABSL_MUST_USE_RESULT bool ok() const { return this->status_.ok(); }
// Returns a reference to our status. If this contains a T, then
// returns absl::OkStatus().
const Status& status() const &;
Status status() &&;
// Returns a reference to the held value if `this->ok()`. Otherwise, throws
// `absl::BadStatusOrAccess` if exception is enabled, or `LOG(FATAL)` if
// exception is 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&;
T& value() &;
const T&& value() const&&;
T&& value() &&;
// 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.
// Alternatively, see value() for a similar API that guarantees
// CHECK-failing if there is no current value.
const T& operator*() const&;
T& operator*() &;
const T&& operator*() const&&;
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;
T* operator->();
// Returns the current value this->ok() == true. Otherwise constructs a value
// using `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) &&;
// 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;
// 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) {
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) {
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_;
}
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);
};
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();
}
template <typename T>
bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) {
return !(lhs == rhs);
}
////////////////////////////////////////////////////////////////////////////////
// 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>
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>
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_
// 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 <array>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/casts.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/types/any.h"
#include "absl/utility/utility.h"
namespace {
using ::testing::AllOf;
using ::testing::AnyWith;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::Ne;
using ::testing::Not;
using ::testing::Pointee;
using ::testing::VariantWith;
#ifdef GTEST_HAS_STATUS_MATCHERS
using ::testing::status::IsOk;
using ::testing::status::IsOkAndHolds;
#else // GTEST_HAS_STATUS_MATCHERS
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();
}
// 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 (!actual_value.ok()) {
*result_listener << "which has status " << actual_value.status();
return false;
}
::testing::StringMatchResultListener inner_listener;
const bool matches =
inner_matcher_.MatchAndExplain(*actual_value, &inner_listener);
const std::string inner_explanation = inner_listener.str();
if (!inner_explanation.empty()) {
*result_listener << "which contains value "
<< ::testing::PrintToString(*actual_value) << ", "
<< inner_explanation;
}
return matches;
}
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::move(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_;
};
// 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>
operator ::testing::Matcher<T>() const { // NOLINT
return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<T>());
}
};
// Macros for testing the results of functions that return absl::Status or
// absl::StatusOr<T> (for any type T).
#define EXPECT_OK(expression) EXPECT_THAT(expression, IsOk())
// Returns a gMock matcher that matches a StatusOr<> whose status is
// OK and whose value matches the inner matcher.
template <typename InnerMatcher>
IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type> IsOkAndHolds(
InnerMatcher&& inner_matcher) {
return IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>(
std::forward<InnerMatcher>(inner_matcher));
}
// Returns a gMock matcher that matches a Status or StatusOr<> which is OK.
inline IsOkMatcher IsOk() { return IsOkMatcher(); }
#endif // GTEST_HAS_STATUS_MATCHERS
struct CopyDetector {
CopyDetector() = default;
explicit CopyDetector(int xx) : x(xx) {}
CopyDetector(CopyDetector&& d) noexcept
: x(d.x), copied(false), moved(true) {}
CopyDetector(const CopyDetector& d) : x(d.x), copied(true), moved(false) {}
CopyDetector& operator=(const CopyDetector& c) {
x = c.x;
copied = true;
moved = false;
return *this;
}
CopyDetector& operator=(CopyDetector&& c) noexcept {
x = c.x;
copied = false;
moved = true;
return *this;
}
int x = 0;
bool copied = false;
bool moved = false;
};
testing::Matcher<const CopyDetector&> CopyDetectorHas(int a, bool b, bool c) {
return AllOf(Field(&CopyDetector::x, a), Field(&CopyDetector::moved, b),
Field(&CopyDetector::copied, c));
}
class Base1 {
public:
virtual ~Base1() {}
int pad;
};
class Base2 {
public:
virtual ~Base2() {}
int yetotherpad;
};
class Derived : public Base1, public Base2 {
public:
virtual ~Derived() {}
int evenmorepad;
};
class CopyNoAssign {
public:
explicit CopyNoAssign(int value) : foo(value) {}
CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {}
int foo;
private:
const CopyNoAssign& operator=(const CopyNoAssign&);
};
absl::StatusOr<std::unique_ptr<int>> ReturnUniquePtr() {
// Uses implicit constructor from T&&
return absl::make_unique<int>(0);
}
TEST(StatusOr, ElementType) {
static_assert(std::is_same<absl::StatusOr<int>::value_type, int>(), "");
static_assert(std::is_same<absl::StatusOr<char>::value_type, char>(), "");
}
TEST(StatusOr, TestMoveOnlyInitialization) {
absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr());
ASSERT_TRUE(thing.ok());
EXPECT_EQ(0, **thing);
int* previous = thing->get();
thing = ReturnUniquePtr();
EXPECT_TRUE(thing.ok());
EXPECT_EQ(0, **thing);
EXPECT_NE(previous, thing->get());
}
TEST(StatusOr, TestMoveOnlyValueExtraction) {
absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr());
ASSERT_TRUE(thing.ok());
std::unique_ptr<int> ptr = *std::move(thing);
EXPECT_EQ(0, *ptr);
thing = std::move(ptr);
ptr = std::move(*thing);
EXPECT_EQ(0, *ptr);
}
TEST(StatusOr, TestMoveOnlyInitializationFromTemporaryByValueOrDie) {
std::unique_ptr<int> ptr(*ReturnUniquePtr());
EXPECT_EQ(0, *ptr);
}
TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) {
static_assert(
std::is_same<const int&&,
decltype(
std::declval<const absl::StatusOr<int>&&>().value())>(),
"value() for const temporaries should return const T&&");
}
TEST(StatusOr, TestMoveOnlyConversion) {
absl::StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr());
EXPECT_TRUE(const_thing.ok());
EXPECT_EQ(0, **const_thing);
// Test rvalue converting assignment
const int* const_previous = const_thing->get();
const_thing = ReturnUniquePtr();
EXPECT_TRUE(const_thing.ok());
EXPECT_EQ(0, **const_thing);
EXPECT_NE(const_previous, const_thing->get());
}
TEST(StatusOr, TestMoveOnlyVector) {
// Sanity check that absl::StatusOr<MoveOnly> works in vector.
std::vector<absl::StatusOr<std::unique_ptr<int>>> vec;
vec.push_back(ReturnUniquePtr());
vec.resize(2);
auto another_vec = std::move(vec);
EXPECT_EQ(0, **another_vec[0]);
EXPECT_EQ(absl::UnknownError(""), another_vec[1].status());
}
TEST(StatusOr, TestDefaultCtor) {
absl::StatusOr<int> thing;
EXPECT_FALSE(thing.ok());
EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown);
}
// Define `EXPECT_DEATH_OR_THROW` to test the behavior of `StatusOr::value`,
// which either throws `BadStatusOrAccess` or `LOG(FATAL)` based on whether
// exceptions are enabled.
#ifdef ABSL_HAVE_EXCEPTIONS
#define EXPECT_DEATH_OR_THROW(statement, status_) \
EXPECT_THROW( \
{ \
try { \
statement; \
} catch (const absl::BadStatusOrAccess& e) { \
EXPECT_EQ(e.status(), status_); \
throw; \
} \
}, \
absl::BadStatusOrAccess);
#else // ABSL_HAVE_EXCEPTIONS
#define EXPECT_DEATH_OR_THROW(statement, status) \
EXPECT_DEATH_IF_SUPPORTED(statement, status.ToString());
#endif // ABSL_HAVE_EXCEPTIONS
TEST(StatusOrDeathTest, TestDefaultCtorValue) {
absl::StatusOr<int> thing;
EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError(""));
const absl::StatusOr<int> thing2;
EXPECT_DEATH_OR_THROW(thing2.value(), absl::UnknownError(""));
}
TEST(StatusOrDeathTest, TestValueNotOk) {
absl::StatusOr<int> thing(absl::CancelledError());
EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError());
}
TEST(StatusOrDeathTest, TestValueNotOkConst) {
const absl::StatusOr<int> thing(absl::UnknownError(""));
EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError(""));
}
TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) {
absl::StatusOr<int*> thing;
EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError(""));
}
TEST(StatusOrDeathTest, TestPointerValueNotOk) {
absl::StatusOr<int*> thing(absl::CancelledError());
EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError());
}
TEST(StatusOrDeathTest, TestPointerValueNotOkConst) {
const absl::StatusOr<int*> thing(absl::CancelledError());
EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError());
}
#if GTEST_HAS_DEATH_TEST
TEST(StatusOrDeathTest, TestStatusCtorStatusOk) {
EXPECT_DEBUG_DEATH(
{
// This will DCHECK
absl::StatusOr<int> thing(absl::OkStatus());
// In optimized mode, we are actually going to get error::INTERNAL for
// status here, rather than crashing, so check that.
EXPECT_FALSE(thing.ok());
EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal);
},
"An OK status is not a valid constructor argument");
}
TEST(StatusOrDeathTest, TestPointerStatusCtorStatusOk) {
EXPECT_DEBUG_DEATH(
{
absl::StatusOr<int*> thing(absl::OkStatus());
// In optimized mode, we are actually going to get error::INTERNAL for
// status here, rather than crashing, so check that.
EXPECT_FALSE(thing.ok());
EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal);
},
"An OK status is not a valid constructor argument");
}
#endif
TEST(StatusOr, ValueAccessor) {
const int kIntValue = 110;
{
absl::StatusOr<int> status_or(kIntValue);
EXPECT_EQ(kIntValue, status_or.value());
EXPECT_EQ(kIntValue, std::move(status_or).value());
}
{
absl::StatusOr<CopyDetector> status_or(kIntValue);
EXPECT_THAT(status_or,
IsOkAndHolds(CopyDetectorHas(kIntValue, false, false)));
CopyDetector copy_detector = status_or.value();
EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, false, true));
copy_detector = std::move(status_or).value();
EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, true, false));
}
}
TEST(StatusOr, BadValueAccess) {
const absl::Status kError = absl::CancelledError("message");
absl::StatusOr<int> status_or(kError);
EXPECT_DEATH_OR_THROW(status_or.value(), kError);
}
TEST(StatusOr, TestStatusCtor) {
absl::StatusOr<int> thing(absl::CancelledError());
EXPECT_FALSE(thing.ok());
EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled);
}
TEST(StatusOr, TestValueCtor) {
const int kI = 4;
const absl::StatusOr<int> thing(kI);
EXPECT_TRUE(thing.ok());
EXPECT_EQ(kI, *thing);
}
struct Foo {
const int x;
explicit Foo(int y) : x(y) {}
};
TEST(StatusOr, InPlaceConstruction) {
EXPECT_THAT(absl::StatusOr<Foo>(absl::in_place, 10),
IsOkAndHolds(Field(&Foo::x, 10)));
}
struct InPlaceHelper {
InPlaceHelper(std::initializer_list<int> xs, std::unique_ptr<int> yy)
: x(xs), y(std::move(yy)) {}
const std::vector<int> x;
std::unique_ptr<int> y;
};
TEST(StatusOr, InPlaceInitListConstruction) {
absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12},
absl::make_unique<int>(13));
EXPECT_THAT(status_or, IsOkAndHolds(AllOf(
Field(&InPlaceHelper::x, ElementsAre(10, 11, 12)),
Field(&InPlaceHelper::y, Pointee(13)))));
}
TEST(StatusOr, Emplace) {
absl::StatusOr<Foo> status_or_foo(10);
status_or_foo.emplace(20);
EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20)));
status_or_foo = absl::InvalidArgumentError("msg");
EXPECT_FALSE(status_or_foo.ok());
EXPECT_EQ(status_or_foo.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status_or_foo.status().message(), "msg");
status_or_foo.emplace(20);
EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20)));
}
TEST(StatusOr, EmplaceInitializerList) {
absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12},
absl::make_unique<int>(13));
status_or.emplace({1, 2, 3}, absl::make_unique<int>(4));
EXPECT_THAT(status_or,
IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)),
Field(&InPlaceHelper::y, Pointee(4)))));
status_or = absl::InvalidArgumentError("msg");
EXPECT_FALSE(status_or.ok());
EXPECT_EQ(status_or.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status_or.status().message(), "msg");
status_or.emplace({1, 2, 3}, absl::make_unique<int>(4));
EXPECT_THAT(status_or,
IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)),
Field(&InPlaceHelper::y, Pointee(4)))));
}
TEST(StatusOr, TestCopyCtorStatusOk) {
const int kI = 4;
const absl::StatusOr<int> original(kI);
const absl::StatusOr<int> copy(original);
EXPECT_OK(copy.status());
EXPECT_EQ(*original, *copy);
}
TEST(StatusOr, TestCopyCtorStatusNotOk) {
absl::StatusOr<int> original(absl::CancelledError());
absl::StatusOr<int> copy(original);
EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled);
}
TEST(StatusOr, TestCopyCtorNonAssignable) {
const int kI = 4;
CopyNoAssign value(kI);
absl::StatusOr<CopyNoAssign> original(value);
absl::StatusOr<CopyNoAssign> copy(original);
EXPECT_OK(copy.status());
EXPECT_EQ(original->foo, copy->foo);
}
TEST(StatusOr, TestCopyCtorStatusOKConverting) {
const int kI = 4;
absl::StatusOr<int> original(kI);
absl::StatusOr<double> copy(original);
EXPECT_OK(copy.status());
EXPECT_DOUBLE_EQ(*original, *copy);
}
TEST(StatusOr, TestCopyCtorStatusNotOkConverting) {
absl::StatusOr<int> original(absl::CancelledError());
absl::StatusOr<double> copy(original);
EXPECT_EQ(copy.status(), original.status());
}
TEST(StatusOr, TestAssignmentStatusOk) {
// Copy assignmment
{
const auto p = std::make_shared<int>(17);
absl::StatusOr<std::shared_ptr<int>> source(p);
absl::StatusOr<std::shared_ptr<int>> target;
target = source;
ASSERT_TRUE(target.ok());
EXPECT_OK(target.status());
EXPECT_EQ(p, *target);
ASSERT_TRUE(source.ok());
EXPECT_OK(source.status());
EXPECT_EQ(p, *source);
}
// Move asssignment
{
const auto p = std::make_shared<int>(17);
absl::StatusOr<std::shared_ptr<int>> source(p);
absl::StatusOr<std::shared_ptr<int>> target;
target = std::move(source);
ASSERT_TRUE(target.ok());
EXPECT_OK(target.status());
EXPECT_EQ(p, *target);
ASSERT_TRUE(source.ok());
EXPECT_OK(source.status());
EXPECT_EQ(nullptr, *source);
}
}
TEST(StatusOr, TestAssignmentStatusNotOk) {
// Copy assignment
{
const absl::Status expected = absl::CancelledError();
absl::StatusOr<int> source(expected);
absl::StatusOr<int> target;
target = source;
EXPECT_FALSE(target.ok());
EXPECT_EQ(expected, target.status());
EXPECT_FALSE(source.ok());
EXPECT_EQ(expected, source.status());
}
// Move assignment
{
const absl::Status expected = absl::CancelledError();
absl::StatusOr<int> source(expected);
absl::StatusOr<int> target;
target = std::move(source);
EXPECT_FALSE(target.ok());
EXPECT_EQ(expected, target.status());
EXPECT_FALSE(source.ok());
EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal);
}
}
TEST(StatusOr, TestAssignmentStatusOKConverting) {
// Copy assignment
{
const int kI = 4;
absl::StatusOr<int> source(kI);
absl::StatusOr<double> target;
target = source;
ASSERT_TRUE(target.ok());
EXPECT_OK(target.status());
EXPECT_DOUBLE_EQ(kI, *target);
ASSERT_TRUE(source.ok());
EXPECT_OK(source.status());
EXPECT_DOUBLE_EQ(kI, *source);
}
// Move assignment
{
const auto p = new int(17);
absl::StatusOr<std::unique_ptr<int>> source(absl::WrapUnique(p));
absl::StatusOr<std::shared_ptr<int>> target;
target = std::move(source);
ASSERT_TRUE(target.ok());
EXPECT_OK(target.status());
EXPECT_EQ(p, target->get());
ASSERT_TRUE(source.ok());
EXPECT_OK(source.status());
EXPECT_EQ(nullptr, source->get());
}
}
struct A {
int x;
};
struct ImplicitConstructibleFromA {
int x;
bool moved;
ImplicitConstructibleFromA(const A& a) // NOLINT
: x(a.x), moved(false) {}
ImplicitConstructibleFromA(A&& a) // NOLINT
: x(a.x), moved(true) {}
};
TEST(StatusOr, ImplicitConvertingConstructor) {
EXPECT_THAT(
absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>(
absl::StatusOr<A>(A{11})),
IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 11),
Field(&ImplicitConstructibleFromA::moved, true))));
absl::StatusOr<A> a(A{12});
EXPECT_THAT(
absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>(a),
IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 12),
Field(&ImplicitConstructibleFromA::moved, false))));
}
struct ExplicitConstructibleFromA {
int x;
bool moved;
explicit ExplicitConstructibleFromA(const A& a) : x(a.x), moved(false) {}
explicit ExplicitConstructibleFromA(A&& a) : x(a.x), moved(true) {}
};
TEST(StatusOr, ExplicitConvertingConstructor) {
EXPECT_FALSE(
(std::is_convertible<const absl::StatusOr<A>&,
absl::StatusOr<ExplicitConstructibleFromA>>::value));
EXPECT_FALSE(
(std::is_convertible<absl::StatusOr<A>&&,
absl::StatusOr<ExplicitConstructibleFromA>>::value));
EXPECT_THAT(
absl::StatusOr<ExplicitConstructibleFromA>(absl::StatusOr<A>(A{11})),
IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 11),
Field(&ExplicitConstructibleFromA::moved, true))));
absl::StatusOr<A> a(A{12});
EXPECT_THAT(
absl::StatusOr<ExplicitConstructibleFromA>(a),
IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 12),
Field(&ExplicitConstructibleFromA::moved, false))));
}
struct ImplicitConstructibleFromBool {
ImplicitConstructibleFromBool(bool y) : x(y) {} // NOLINT
bool x = false;
};
struct ConvertibleToBool {
explicit ConvertibleToBool(bool y) : x(y) {}
operator bool() const { return x; } // NOLINT
bool x = false;
};
TEST(StatusOr, ImplicitBooleanConstructionWithImplicitCasts) {
EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)),
IsOkAndHolds(true));
EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)),
IsOkAndHolds(false));
EXPECT_THAT(
absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromBool>>(
absl::StatusOr<bool>(false)),
IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false)));
EXPECT_FALSE((std::is_convertible<
absl::StatusOr<ConvertibleToBool>,
absl::StatusOr<ImplicitConstructibleFromBool>>::value));
}
TEST(StatusOr, BooleanConstructionWithImplicitCasts) {
EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)),
IsOkAndHolds(true));
EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)),
IsOkAndHolds(false));
EXPECT_THAT(
absl::StatusOr<ImplicitConstructibleFromBool>{
absl::StatusOr<bool>(false)},
IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false)));
EXPECT_THAT(
absl::StatusOr<ImplicitConstructibleFromBool>{
absl::StatusOr<bool>(absl::InvalidArgumentError(""))},
Not(IsOk()));
EXPECT_THAT(
absl::StatusOr<ImplicitConstructibleFromBool>{
absl::StatusOr<ConvertibleToBool>(ConvertibleToBool{false})},
IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false)));
EXPECT_THAT(
absl::StatusOr<ImplicitConstructibleFromBool>{
absl::StatusOr<ConvertibleToBool>(absl::InvalidArgumentError(""))},
Not(IsOk()));
}
TEST(StatusOr, ConstImplicitCast) {
EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>(
absl::StatusOr<const bool>(true)),
IsOkAndHolds(true));
EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>(
absl::StatusOr<const bool>(false)),
IsOkAndHolds(false));
EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>(
absl::StatusOr<bool>(true)),
IsOkAndHolds(true));
EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>(
absl::StatusOr<bool>(false)),
IsOkAndHolds(false));
EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const std::string>>(
absl::StatusOr<std::string>("foo")),
IsOkAndHolds("foo"));
EXPECT_THAT(absl::implicit_cast<absl::StatusOr<std::string>>(
absl::StatusOr<const std::string>("foo")),
IsOkAndHolds("foo"));
EXPECT_THAT(
absl::implicit_cast<absl::StatusOr<std::shared_ptr<const std::string>>>(
absl::StatusOr<std::shared_ptr<std::string>>(
std::make_shared<std::string>("foo"))),
IsOkAndHolds(Pointee(std::string("foo"))));
}
TEST(StatusOr, ConstExplicitConstruction) {
EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(true)),
IsOkAndHolds(true));
EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(false)),
IsOkAndHolds(false));
EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(true)),
IsOkAndHolds(true));
EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(false)),
IsOkAndHolds(false));
}
struct ExplicitConstructibleFromInt {
int x;
explicit ExplicitConstructibleFromInt(int y) : x(y) {}
};
TEST(StatusOr, ExplicitConstruction) {
EXPECT_THAT(absl::StatusOr<ExplicitConstructibleFromInt>(10),
IsOkAndHolds(Field(&ExplicitConstructibleFromInt::x, 10)));
}
TEST(StatusOr, ImplicitConstruction) {
// Check implicit casting works.
auto status_or =
absl::implicit_cast<absl::StatusOr<absl::variant<int, std::string>>>(10);
EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10)));
}
TEST(StatusOr, ImplicitConstructionFromInitliazerList) {
// Note: dropping the explicit std::initializer_list<int> is not supported
// by absl::StatusOr or absl::optional.
auto status_or =
absl::implicit_cast<absl::StatusOr<std::vector<int>>>({{10, 20, 30}});
EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30)));
}
TEST(StatusOr, UniquePtrImplicitConstruction) {
auto status_or = absl::implicit_cast<absl::StatusOr<std::unique_ptr<Base1>>>(
absl::make_unique<Derived>());
EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr)));
}
TEST(StatusOr, NestedStatusOrCopyAndMoveConstructorTests) {
absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10);
absl::StatusOr<absl::StatusOr<CopyDetector>> status_error =
absl::InvalidArgumentError("foo");
EXPECT_THAT(status_or,
IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false))));
absl::StatusOr<absl::StatusOr<CopyDetector>> a = status_or;
EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true))));
absl::StatusOr<absl::StatusOr<CopyDetector>> a_err = status_error;
EXPECT_THAT(a_err, Not(IsOk()));
const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or;
absl::StatusOr<absl::StatusOr<CopyDetector>> b = cref; // NOLINT
EXPECT_THAT(b, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true))));
const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error;
absl::StatusOr<absl::StatusOr<CopyDetector>> b_err = cref_err; // NOLINT
EXPECT_THAT(b_err, Not(IsOk()));
absl::StatusOr<absl::StatusOr<CopyDetector>> c = std::move(status_or);
EXPECT_THAT(c, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false))));
absl::StatusOr<absl::StatusOr<CopyDetector>> c_err = std::move(status_error);
EXPECT_THAT(c_err, Not(IsOk()));
}
TEST(StatusOr, NestedStatusOrCopyAndMoveAssignment) {
absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10);
absl::StatusOr<absl::StatusOr<CopyDetector>> status_error =
absl::InvalidArgumentError("foo");
absl::StatusOr<absl::StatusOr<CopyDetector>> a;
a = status_or;
EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true))));
a = status_error;
EXPECT_THAT(a, Not(IsOk()));
const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or;
a = cref;
EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true))));
const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error;
a = cref_err;
EXPECT_THAT(a, Not(IsOk()));
a = std::move(status_or);
EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false))));
a = std::move(status_error);
EXPECT_THAT(a, Not(IsOk()));
}
struct Copyable {
Copyable() {}
Copyable(const Copyable&) {}
Copyable& operator=(const Copyable&) { return *this; }
};
struct MoveOnly {
MoveOnly() {}
MoveOnly(MoveOnly&&) {}
MoveOnly& operator=(MoveOnly&&) { return *this; }
};
struct NonMovable {
NonMovable() {}
NonMovable(const NonMovable&) = delete;
NonMovable(NonMovable&&) = delete;
NonMovable& operator=(const NonMovable&) = delete;
NonMovable& operator=(NonMovable&&) = delete;
};
TEST(StatusOr, CopyAndMoveAbility) {
EXPECT_TRUE(std::is_copy_constructible<Copyable>::value);
EXPECT_TRUE(std::is_copy_assignable<Copyable>::value);
EXPECT_TRUE(std::is_move_constructible<Copyable>::value);
EXPECT_TRUE(std::is_move_assignable<Copyable>::value);
EXPECT_FALSE(std::is_copy_constructible<MoveOnly>::value);
EXPECT_FALSE(std::is_copy_assignable<MoveOnly>::value);
EXPECT_TRUE(std::is_move_constructible<MoveOnly>::value);
EXPECT_TRUE(std::is_move_assignable<MoveOnly>::value);
EXPECT_FALSE(std::is_copy_constructible<NonMovable>::value);
EXPECT_FALSE(std::is_copy_assignable<NonMovable>::value);
EXPECT_FALSE(std::is_move_constructible<NonMovable>::value);
EXPECT_FALSE(std::is_move_assignable<NonMovable>::value);
}
TEST(StatusOr, StatusOrAnyCopyAndMoveConstructorTests) {
absl::StatusOr<absl::any> status_or = CopyDetector(10);
absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo");
EXPECT_THAT(
status_or,
IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false))));
absl::StatusOr<absl::any> a = status_or;
EXPECT_THAT(
a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true))));
absl::StatusOr<absl::any> a_err = status_error;
EXPECT_THAT(a_err, Not(IsOk()));
const absl::StatusOr<absl::any>& cref = status_or;
// No lint for no-change copy.
absl::StatusOr<absl::any> b = cref; // NOLINT
EXPECT_THAT(
b, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true))));
const absl::StatusOr<absl::any>& cref_err = status_error;
// No lint for no-change copy.
absl::StatusOr<absl::any> b_err = cref_err; // NOLINT
EXPECT_THAT(b_err, Not(IsOk()));
absl::StatusOr<absl::any> c = std::move(status_or);
EXPECT_THAT(
c, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false))));
absl::StatusOr<absl::any> c_err = std::move(status_error);
EXPECT_THAT(c_err, Not(IsOk()));
}
TEST(StatusOr, StatusOrAnyCopyAndMoveAssignment) {
absl::StatusOr<absl::any> status_or = CopyDetector(10);
absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo");
absl::StatusOr<absl::any> a;
a = status_or;
EXPECT_THAT(
a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true))));
a = status_error;
EXPECT_THAT(a, Not(IsOk()));
const absl::StatusOr<absl::any>& cref = status_or;
a = cref;
EXPECT_THAT(
a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true))));
const absl::StatusOr<absl::any>& cref_err = status_error;
a = cref_err;
EXPECT_THAT(a, Not(IsOk()));
a = std::move(status_or);
EXPECT_THAT(
a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false))));
a = std::move(status_error);
EXPECT_THAT(a, Not(IsOk()));
}
TEST(StatusOr, StatusOrCopyAndMoveTestsConstructor) {
absl::StatusOr<CopyDetector> status_or(10);
ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false)));
absl::StatusOr<CopyDetector> a(status_or);
EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true)));
const absl::StatusOr<CopyDetector>& cref = status_or;
absl::StatusOr<CopyDetector> b(cref); // NOLINT
EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true)));
absl::StatusOr<CopyDetector> c(std::move(status_or));
EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false)));
}
TEST(StatusOr, StatusOrCopyAndMoveTestsAssignment) {
absl::StatusOr<CopyDetector> status_or(10);
ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false)));
absl::StatusOr<CopyDetector> a;
a = status_or;
EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true)));
const absl::StatusOr<CopyDetector>& cref = status_or;
absl::StatusOr<CopyDetector> b;
b = cref;
EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true)));
absl::StatusOr<CopyDetector> c;
c = std::move(status_or);
EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false)));
}
TEST(StatusOr, AbslAnyAssignment) {
EXPECT_FALSE((std::is_assignable<absl::StatusOr<absl::any>,
absl::StatusOr<int>>::value));
absl::StatusOr<absl::any> status_or;
status_or = absl::InvalidArgumentError("foo");
EXPECT_THAT(status_or, Not(IsOk()));
}
TEST(StatusOr, ImplicitAssignment) {
absl::StatusOr<absl::variant<int, std::string>> status_or;
status_or = 10;
EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10)));
}
TEST(StatusOr, SelfDirectInitAssignment) {
absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}};
status_or = *status_or;
EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30)));
}
TEST(StatusOr, ImplicitCastFromInitializerList) {
absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}};
EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30)));
}
TEST(StatusOr, UniquePtrImplicitAssignment) {
absl::StatusOr<std::unique_ptr<Base1>> status_or;
status_or = absl::make_unique<Derived>();
EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr)));
}
TEST(StatusOr, Pointer) {
struct A {};
struct B : public A {};
struct C : private A {};
EXPECT_TRUE((std::is_constructible<absl::StatusOr<A*>, B*>::value));
EXPECT_TRUE((std::is_convertible<B*, absl::StatusOr<A*>>::value));
EXPECT_FALSE((std::is_constructible<absl::StatusOr<A*>, C*>::value));
EXPECT_FALSE((std::is_convertible<C*, absl::StatusOr<A*>>::value));
}
TEST(StatusOr, TestAssignmentStatusNotOkConverting) {
// Copy assignment
{
const absl::Status expected = absl::CancelledError();
absl::StatusOr<int> source(expected);
absl::StatusOr<double> target;
target = source;
EXPECT_FALSE(target.ok());
EXPECT_EQ(expected, target.status());
EXPECT_FALSE(source.ok());
EXPECT_EQ(expected, source.status());
}
// Move assignment
{
const absl::Status expected = absl::CancelledError();
absl::StatusOr<int> source(expected);
absl::StatusOr<double> target;
target = std::move(source);
EXPECT_FALSE(target.ok());
EXPECT_EQ(expected, target.status());
EXPECT_FALSE(source.ok());
EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal);
}
}
TEST(StatusOr, SelfAssignment) {
// Copy-assignment, status OK
{
// A string long enough that it's likely to defeat any inline representation
// optimization.
const std::string long_str(128, 'a');
absl::StatusOr<std::string> so = long_str;
so = *&so;
ASSERT_TRUE(so.ok());
EXPECT_OK(so.status());
EXPECT_EQ(long_str, *so);
}
// Copy-assignment, error status
{
absl::StatusOr<int> so = absl::NotFoundError("taco");
so = *&so;
EXPECT_FALSE(so.ok());
EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound);
EXPECT_EQ(so.status().message(), "taco");
}
// Move-assignment with copyable type, status OK
{
absl::StatusOr<int> so = 17;
// Fool the compiler, which otherwise complains.
auto& same = so;
so = std::move(same);
ASSERT_TRUE(so.ok());
EXPECT_OK(so.status());
EXPECT_EQ(17, *so);
}
// Move-assignment with copyable type, error status
{
absl::StatusOr<int> so = absl::NotFoundError("taco");
// Fool the compiler, which otherwise complains.
auto& same = so;
so = std::move(same);
EXPECT_FALSE(so.ok());
EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound);
EXPECT_EQ(so.status().message(), "taco");
}
// Move-assignment with non-copyable type, status OK
{
const auto raw = new int(17);
absl::StatusOr<std::unique_ptr<int>> so = absl::WrapUnique(raw);
// Fool the compiler, which otherwise complains.
auto& same = so;
so = std::move(same);
ASSERT_TRUE(so.ok());
EXPECT_OK(so.status());
EXPECT_EQ(raw, so->get());
}
// Move-assignment with non-copyable type, error status
{
absl::StatusOr<std::unique_ptr<int>> so = absl::NotFoundError("taco");
// Fool the compiler, which otherwise complains.
auto& same = so;
so = std::move(same);
EXPECT_FALSE(so.ok());
EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound);
EXPECT_EQ(so.status().message(), "taco");
}
}
// These types form the overload sets of the constructors and the assignment
// operators of `MockValue`. They distinguish construction from assignment,
// lvalue from rvalue.
struct FromConstructibleAssignableLvalue {};
struct FromConstructibleAssignableRvalue {};
struct FromImplicitConstructibleOnly {};
struct FromAssignableOnly {};
// This class is for testing the forwarding value assignments of `StatusOr`.
// `from_rvalue` indicates whether the constructor or the assignment taking
// rvalue reference is called. `from_assignment` indicates whether any
// assignment is called.
struct MockValue {
// Constructs `MockValue` from `FromConstructibleAssignableLvalue`.
MockValue(const FromConstructibleAssignableLvalue&) // NOLINT
: from_rvalue(false), assigned(false) {}
// Constructs `MockValue` from `FromConstructibleAssignableRvalue`.
MockValue(FromConstructibleAssignableRvalue&&) // NOLINT
: from_rvalue(true), assigned(false) {}
// Constructs `MockValue` from `FromImplicitConstructibleOnly`.
// `MockValue` is not assignable from `FromImplicitConstructibleOnly`.
MockValue(const FromImplicitConstructibleOnly&) // NOLINT
: from_rvalue(false), assigned(false) {}
// Assigns `FromConstructibleAssignableLvalue`.
MockValue& operator=(const FromConstructibleAssignableLvalue&) {
from_rvalue = false;
assigned = true;
return *this;
}
// Assigns `FromConstructibleAssignableRvalue` (rvalue only).
MockValue& operator=(FromConstructibleAssignableRvalue&&) {
from_rvalue = true;
assigned = true;
return *this;
}
// Assigns `FromAssignableOnly`, but not constructible from
// `FromAssignableOnly`.
MockValue& operator=(const FromAssignableOnly&) {
from_rvalue = false;
assigned = true;
return *this;
}
bool from_rvalue;
bool assigned;
};
// operator=(U&&)
TEST(StatusOr, PerfectForwardingAssignment) {
// U == T
constexpr int kValue1 = 10, kValue2 = 20;
absl::StatusOr<CopyDetector> status_or;
CopyDetector lvalue(kValue1);
status_or = lvalue;
EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue1, false, true)));
status_or = CopyDetector(kValue2);
EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue2, true, false)));
// U != T
EXPECT_TRUE(
(std::is_assignable<absl::StatusOr<MockValue>&,
const FromConstructibleAssignableLvalue&>::value));
EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&,
FromConstructibleAssignableLvalue&&>::value));
EXPECT_FALSE(
(std::is_assignable<absl::StatusOr<MockValue>&,
const FromConstructibleAssignableRvalue&>::value));
EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&,
FromConstructibleAssignableRvalue&&>::value));
EXPECT_TRUE(
(std::is_assignable<absl::StatusOr<MockValue>&,
const FromImplicitConstructibleOnly&>::value));
EXPECT_FALSE((std::is_assignable<absl::StatusOr<MockValue>&,
const FromAssignableOnly&>::value));
absl::StatusOr<MockValue> from_lvalue(FromConstructibleAssignableLvalue{});
EXPECT_FALSE(from_lvalue->from_rvalue);
EXPECT_FALSE(from_lvalue->assigned);
from_lvalue = FromConstructibleAssignableLvalue{};
EXPECT_FALSE(from_lvalue->from_rvalue);
EXPECT_TRUE(from_lvalue->assigned);
absl::StatusOr<MockValue> from_rvalue(FromConstructibleAssignableRvalue{});
EXPECT_TRUE(from_rvalue->from_rvalue);
EXPECT_FALSE(from_rvalue->assigned);
from_rvalue = FromConstructibleAssignableRvalue{};
EXPECT_TRUE(from_rvalue->from_rvalue);
EXPECT_TRUE(from_rvalue->assigned);
absl::StatusOr<MockValue> from_implicit_constructible(
FromImplicitConstructibleOnly{});
EXPECT_FALSE(from_implicit_constructible->from_rvalue);
EXPECT_FALSE(from_implicit_constructible->assigned);
// construct a temporary `StatusOr` object and invoke the `StatusOr` move
// assignment operator.
from_implicit_constructible = FromImplicitConstructibleOnly{};
EXPECT_FALSE(from_implicit_constructible->from_rvalue);
EXPECT_FALSE(from_implicit_constructible->assigned);
}
TEST(StatusOr, TestStatus) {
absl::StatusOr<int> good(4);
EXPECT_TRUE(good.ok());
absl::StatusOr<int> bad(absl::CancelledError());
EXPECT_FALSE(bad.ok());
EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled);
}
TEST(StatusOr, OperatorStarRefQualifiers) {
static_assert(
std::is_same<const int&,
decltype(*std::declval<const absl::StatusOr<int>&>())>(),
"Unexpected ref-qualifiers");
static_assert(
std::is_same<int&, decltype(*std::declval<absl::StatusOr<int>&>())>(),
"Unexpected ref-qualifiers");
static_assert(
std::is_same<const int&&,
decltype(*std::declval<const absl::StatusOr<int>&&>())>(),
"Unexpected ref-qualifiers");
static_assert(
std::is_same<int&&, decltype(*std::declval<absl::StatusOr<int>&&>())>(),
"Unexpected ref-qualifiers");
}
TEST(StatusOr, OperatorStar) {
const absl::StatusOr<std::string> const_lvalue("hello");
EXPECT_EQ("hello", *const_lvalue);
absl::StatusOr<std::string> lvalue("hello");
EXPECT_EQ("hello", *lvalue);
// Note: Recall that std::move() is equivalent to a static_cast to an rvalue
// reference type.
const absl::StatusOr<std::string> const_rvalue("hello");
EXPECT_EQ("hello", *std::move(const_rvalue)); // NOLINT
absl::StatusOr<std::string> rvalue("hello");
EXPECT_EQ("hello", *std::move(rvalue));
}
TEST(StatusOr, OperatorArrowQualifiers) {
static_assert(
std::is_same<
const int*,
decltype(std::declval<const absl::StatusOr<int>&>().operator->())>(),
"Unexpected qualifiers");
static_assert(
std::is_same<
int*, decltype(std::declval<absl::StatusOr<int>&>().operator->())>(),
"Unexpected qualifiers");
static_assert(
std::is_same<
const int*,
decltype(std::declval<const absl::StatusOr<int>&&>().operator->())>(),
"Unexpected qualifiers");
static_assert(
std::is_same<
int*, decltype(std::declval<absl::StatusOr<int>&&>().operator->())>(),
"Unexpected qualifiers");
}
TEST(StatusOr, OperatorArrow) {
const absl::StatusOr<std::string> const_lvalue("hello");
EXPECT_EQ(std::string("hello"), const_lvalue->c_str());
absl::StatusOr<std::string> lvalue("hello");
EXPECT_EQ(std::string("hello"), lvalue->c_str());
}
TEST(StatusOr, RValueStatus) {
absl::StatusOr<int> so(absl::NotFoundError("taco"));
const absl::Status s = std::move(so).status();
EXPECT_EQ(s.code(), absl::StatusCode::kNotFound);
EXPECT_EQ(s.message(), "taco");
// Check that !ok() still implies !status().ok(), even after moving out of the
// object. See the note on the rvalue ref-qualified status method.
EXPECT_FALSE(so.ok()); // NOLINT
EXPECT_FALSE(so.status().ok());
EXPECT_EQ(so.status().code(), absl::StatusCode::kInternal);
EXPECT_EQ(so.status().message(), "Status accessed after move.");
}
TEST(StatusOr, TestValue) {
const int kI = 4;
absl::StatusOr<int> thing(kI);
EXPECT_EQ(kI, *thing);
}
TEST(StatusOr, TestValueConst) {
const int kI = 4;
const absl::StatusOr<int> thing(kI);
EXPECT_EQ(kI, *thing);
}
TEST(StatusOr, TestPointerDefaultCtor) {
absl::StatusOr<int*> thing;
EXPECT_FALSE(thing.ok());
EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown);
}
TEST(StatusOr, TestPointerStatusCtor) {
absl::StatusOr<int*> thing(absl::CancelledError());
EXPECT_FALSE(thing.ok());
EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled);
}
TEST(StatusOr, TestPointerValueCtor) {
const int kI = 4;
// Construction from a non-null pointer
{
absl::StatusOr<const int*> so(&kI);
EXPECT_TRUE(so.ok());
EXPECT_OK(so.status());
EXPECT_EQ(&kI, *so);
}
// Construction from a null pointer constant
{
absl::StatusOr<const int*> so(nullptr);
EXPECT_TRUE(so.ok());
EXPECT_OK(so.status());
EXPECT_EQ(nullptr, *so);
}
// Construction from a non-literal null pointer
{
const int* const p = nullptr;
absl::StatusOr<const int*> so(p);
EXPECT_TRUE(so.ok());
EXPECT_OK(so.status());
EXPECT_EQ(nullptr, *so);
}
}
TEST(StatusOr, TestPointerCopyCtorStatusOk) {
const int kI = 0;
absl::StatusOr<const int*> original(&kI);
absl::StatusOr<const int*> copy(original);
EXPECT_OK(copy.status());
EXPECT_EQ(*original, *copy);
}
TEST(StatusOr, TestPointerCopyCtorStatusNotOk) {
absl::StatusOr<int*> original(absl::CancelledError());
absl::StatusOr<int*> copy(original);
EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled);
}
TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) {
Derived derived;
absl::StatusOr<Derived*> original(&derived);
absl::StatusOr<Base2*> copy(original);
EXPECT_OK(copy.status());
EXPECT_EQ(static_cast<const Base2*>(*original), *copy);
}
TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) {
absl::StatusOr<Derived*> original(absl::CancelledError());
absl::StatusOr<Base2*> copy(original);
EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled);
}
TEST(StatusOr, TestPointerAssignmentStatusOk) {
const int kI = 0;
absl::StatusOr<const int*> source(&kI);
absl::StatusOr<const int*> target;
target = source;
EXPECT_OK(target.status());
EXPECT_EQ(*source, *target);
}
TEST(StatusOr, TestPointerAssignmentStatusNotOk) {
absl::StatusOr<int*> source(absl::CancelledError());
absl::StatusOr<int*> target;
target = source;
EXPECT_EQ(target.status().code(), absl::StatusCode::kCancelled);
}
TEST(StatusOr, TestPointerAssignmentStatusOKConverting) {
Derived derived;
absl::StatusOr<Derived*> source(&derived);
absl::StatusOr<Base2*> target;
target = source;
EXPECT_OK(target.status());
EXPECT_EQ(static_cast<const Base2*>(*source), *target);
}
TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) {
absl::StatusOr<Derived*> source(absl::CancelledError());
absl::StatusOr<Base2*> target;
target = source;
EXPECT_EQ(target.status(), source.status());
}
TEST(StatusOr, TestPointerStatus) {
const int kI = 0;
absl::StatusOr<const int*> good(&kI);
EXPECT_TRUE(good.ok());
absl::StatusOr<const int*> bad(absl::CancelledError());
EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled);
}
TEST(StatusOr, TestPointerValue) {
const int kI = 0;
absl::StatusOr<const int*> thing(&kI);
EXPECT_EQ(&kI, *thing);
}
TEST(StatusOr, TestPointerValueConst) {
const int kI = 0;
const absl::StatusOr<const int*> thing(&kI);
EXPECT_EQ(&kI, *thing);
}
TEST(StatusOr, StatusOrVectorOfUniquePointerCanReserveAndResize) {
using EvilType = std::vector<std::unique_ptr<int>>;
static_assert(std::is_copy_constructible<EvilType>::value, "");
std::vector<::absl::StatusOr<EvilType>> v(5);
v.reserve(v.capacity() + 10);
v.resize(v.capacity() + 10);
}
TEST(StatusOr, ConstPayload) {
// A reduced version of a problematic type found in the wild. All of the
// operations below should compile.
absl::StatusOr<const int> a;
// Copy-construction
absl::StatusOr<const int> b(a);
// Copy-assignment
EXPECT_FALSE(std::is_copy_assignable<absl::StatusOr<const int>>::value);
// Move-construction
absl::StatusOr<const int> c(std::move(a));
// Move-assignment
EXPECT_FALSE(std::is_move_assignable<absl::StatusOr<const int>>::value);
}
TEST(StatusOr, 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, absl::StatusOr<std::unique_ptr<int>>>;
MapType a;
// Move-construction
MapType b(std::move(a));
// Move-assignment
a = std::move(b);
}
TEST(StatusOr, ValueOrOk) {
const absl::StatusOr<int> status_or = 0;
EXPECT_EQ(status_or.value_or(-1), 0);
}
TEST(StatusOr, ValueOrDefault) {
const absl::StatusOr<int> status_or = absl::CancelledError();
EXPECT_EQ(status_or.value_or(-1), -1);
}
TEST(StatusOr, MoveOnlyValueOrOk) {
EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(0))
.value_or(absl::make_unique<int>(-1)),
Pointee(0));
}
TEST(StatusOr, MoveOnlyValueOrDefault) {
EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::CancelledError())
.value_or(absl::make_unique<int>(-1)),
Pointee(-1));
}
static absl::StatusOr<int> MakeStatus() { return 100; }
TEST(StatusOr, TestIgnoreError) { MakeStatus().IgnoreError(); }
TEST(StatusOr, EqualityOperator) {
constexpr int kNumCases = 4;
std::array<absl::StatusOr<int>, kNumCases> group1 = {
absl::StatusOr<int>(1), absl::StatusOr<int>(2),
absl::StatusOr<int>(absl::InvalidArgumentError("msg")),
absl::StatusOr<int>(absl::InternalError("msg"))};
std::array<absl::StatusOr<int>, kNumCases> group2 = {
absl::StatusOr<int>(1), absl::StatusOr<int>(2),
absl::StatusOr<int>(absl::InvalidArgumentError("msg")),
absl::StatusOr<int>(absl::InternalError("msg"))};
for (int i = 0; i < kNumCases; ++i) {
for (int j = 0; j < kNumCases; ++j) {
if (i == j) {
EXPECT_TRUE(group1[i] == group2[j]);
EXPECT_FALSE(group1[i] != group2[j]);
} else {
EXPECT_FALSE(group1[i] == group2[j]);
EXPECT_TRUE(group1[i] != group2[j]);
}
}
}
}
struct MyType {
bool operator==(const MyType&) const { return true; }
};
enum class ConvTraits { kNone = 0, kImplicit = 1, kExplicit = 2 };
// This class has conversion operator to `StatusOr<T>` based on value of
// `conv_traits`.
template <typename T, ConvTraits conv_traits = ConvTraits::kNone>
struct StatusOrConversionBase {};
template <typename T>
struct StatusOrConversionBase<T, ConvTraits::kImplicit> {
operator absl::StatusOr<T>() const& { // NOLINT
return absl::InvalidArgumentError("conversion to absl::StatusOr");
}
operator absl::StatusOr<T>() && { // NOLINT
return absl::InvalidArgumentError("conversion to absl::StatusOr");
}
};
template <typename T>
struct StatusOrConversionBase<T, ConvTraits::kExplicit> {
explicit operator absl::StatusOr<T>() const& {
return absl::InvalidArgumentError("conversion to absl::StatusOr");
}
explicit operator absl::StatusOr<T>() && {
return absl::InvalidArgumentError("conversion to absl::StatusOr");
}
};
// This class has conversion operator to `T` based on the value of
// `conv_traits`.
template <typename T, ConvTraits conv_traits = ConvTraits::kNone>
struct ConversionBase {};
template <typename T>
struct ConversionBase<T, ConvTraits::kImplicit> {
operator T() const& { return t; } // NOLINT
operator T() && { return std::move(t); } // NOLINT
T t;
};
template <typename T>
struct ConversionBase<T, ConvTraits::kExplicit> {
explicit operator T() const& { return t; }
explicit operator T() && { return std::move(t); }
T t;
};
// This class has conversion operator to `absl::Status` based on the value of
// `conv_traits`.
template <ConvTraits conv_traits = ConvTraits::kNone>
struct StatusConversionBase {};
template <>
struct StatusConversionBase<ConvTraits::kImplicit> {
operator absl::Status() const& { // NOLINT
return absl::InternalError("conversion to Status");
}
operator absl::Status() && { // NOLINT
return absl::InternalError("conversion to Status");
}
};
template <>
struct StatusConversionBase<ConvTraits::kExplicit> {
explicit operator absl::Status() const& { // NOLINT
return absl::InternalError("conversion to Status");
}
explicit operator absl::Status() && { // NOLINT
return absl::InternalError("conversion to Status");
}
};
static constexpr int kConvToStatus = 1;
static constexpr int kConvToStatusOr = 2;
static constexpr int kConvToT = 4;
static constexpr int kConvExplicit = 8;
constexpr ConvTraits GetConvTraits(int bit, int config) {
return (config & bit) == 0
? ConvTraits::kNone
: ((config & kConvExplicit) == 0 ? ConvTraits::kImplicit
: ConvTraits::kExplicit);
}
// This class conditionally has conversion operator to `absl::Status`, `T`,
// `StatusOr<T>`, based on values of the template parameters.
template <typename T, int config>
struct CustomType
: StatusOrConversionBase<T, GetConvTraits(kConvToStatusOr, config)>,
ConversionBase<T, GetConvTraits(kConvToT, config)>,
StatusConversionBase<GetConvTraits(kConvToStatus, config)> {};
struct ConvertibleToAnyStatusOr {
template <typename T>
operator absl::StatusOr<T>() const { // NOLINT
return absl::InvalidArgumentError("Conversion to absl::StatusOr");
}
};
// Test the rank of overload resolution for `StatusOr<T>` constructor and
// assignment, from highest to lowest:
// 1. T/Status
// 2. U that has conversion operator to absl::StatusOr<T>
// 3. U that is convertible to Status
// 4. U that is convertible to T
TEST(StatusOr, ConstructionFromT) {
// Construct absl::StatusOr<T> from T when T is convertible to
// absl::StatusOr<T>
{
ConvertibleToAnyStatusOr v;
absl::StatusOr<ConvertibleToAnyStatusOr> statusor(v);
EXPECT_TRUE(statusor.ok());
}
{
ConvertibleToAnyStatusOr v;
absl::StatusOr<ConvertibleToAnyStatusOr> statusor = v;
EXPECT_TRUE(statusor.ok());
}
// Construct absl::StatusOr<T> from T when T is explicitly convertible to
// Status
{
CustomType<MyType, kConvToStatus | kConvExplicit> v;
absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor(
v);
EXPECT_TRUE(statusor.ok());
}
{
CustomType<MyType, kConvToStatus | kConvExplicit> v;
absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor =
v;
EXPECT_TRUE(statusor.ok());
}
}
// Construct absl::StatusOr<T> from U when U is explicitly convertible to T
TEST(StatusOr, ConstructionFromTypeConvertibleToT) {
{
CustomType<MyType, kConvToT | kConvExplicit> v;
absl::StatusOr<MyType> statusor(v);
EXPECT_TRUE(statusor.ok());
}
{
CustomType<MyType, kConvToT> v;
absl::StatusOr<MyType> statusor = v;
EXPECT_TRUE(statusor.ok());
}
}
// Construct absl::StatusOr<T> from U when U has explicit conversion operator to
// absl::StatusOr<T>
TEST(StatusOr, ConstructionFromTypeWithConversionOperatorToStatusOrT) {
{
CustomType<MyType, kConvToStatusOr | kConvExplicit> v;
absl::StatusOr<MyType> statusor(v);
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToT | kConvToStatusOr | kConvExplicit> v;
absl::StatusOr<MyType> statusor(v);
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToStatusOr | kConvToStatus | kConvExplicit> v;
absl::StatusOr<MyType> statusor(v);
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType,
kConvToT | kConvToStatusOr | kConvToStatus | kConvExplicit>
v;
absl::StatusOr<MyType> statusor(v);
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToStatusOr> v;
absl::StatusOr<MyType> statusor = v;
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToT | kConvToStatusOr> v;
absl::StatusOr<MyType> statusor = v;
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToStatusOr | kConvToStatus> v;
absl::StatusOr<MyType> statusor = v;
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v;
absl::StatusOr<MyType> statusor = v;
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
}
TEST(StatusOr, ConstructionFromTypeConvertibleToStatus) {
// Construction fails because conversion to `Status` is explicit.
{
CustomType<MyType, kConvToStatus | kConvExplicit> v;
absl::StatusOr<MyType> statusor(v);
EXPECT_FALSE(statusor.ok());
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
}
{
CustomType<MyType, kConvToT | kConvToStatus | kConvExplicit> v;
absl::StatusOr<MyType> statusor(v);
EXPECT_FALSE(statusor.ok());
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
}
{
CustomType<MyType, kConvToStatus> v;
absl::StatusOr<MyType> statusor = v;
EXPECT_FALSE(statusor.ok());
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
}
{
CustomType<MyType, kConvToT | kConvToStatus> v;
absl::StatusOr<MyType> statusor = v;
EXPECT_FALSE(statusor.ok());
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
}
}
TEST(StatusOr, AssignmentFromT) {
// Assign to absl::StatusOr<T> from T when T is convertible to
// absl::StatusOr<T>
{
ConvertibleToAnyStatusOr v;
absl::StatusOr<ConvertibleToAnyStatusOr> statusor;
statusor = v;
EXPECT_TRUE(statusor.ok());
}
// Assign to absl::StatusOr<T> from T when T is convertible to Status
{
CustomType<MyType, kConvToStatus> v;
absl::StatusOr<CustomType<MyType, kConvToStatus>> statusor;
statusor = v;
EXPECT_TRUE(statusor.ok());
}
}
TEST(StatusOr, AssignmentFromTypeConvertibleToT) {
// Assign to absl::StatusOr<T> from U when U is convertible to T
{
CustomType<MyType, kConvToT> v;
absl::StatusOr<MyType> statusor;
statusor = v;
EXPECT_TRUE(statusor.ok());
}
}
TEST(StatusOr, AssignmentFromTypeWithConversionOperatortoStatusOrT) {
// Assign to absl::StatusOr<T> from U when U has conversion operator to
// absl::StatusOr<T>
{
CustomType<MyType, kConvToStatusOr> v;
absl::StatusOr<MyType> statusor;
statusor = v;
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToT | kConvToStatusOr> v;
absl::StatusOr<MyType> statusor;
statusor = v;
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToStatusOr | kConvToStatus> v;
absl::StatusOr<MyType> statusor;
statusor = v;
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
{
CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v;
absl::StatusOr<MyType> statusor;
statusor = v;
EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>());
}
}
TEST(StatusOr, AssignmentFromTypeConvertibleToStatus) {
// Assign to absl::StatusOr<T> from U when U is convertible to Status
{
CustomType<MyType, kConvToStatus> v;
absl::StatusOr<MyType> statusor;
statusor = v;
EXPECT_FALSE(statusor.ok());
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
}
{
CustomType<MyType, kConvToT | kConvToStatus> v;
absl::StatusOr<MyType> statusor;
statusor = v;
EXPECT_FALSE(statusor.ok());
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
}
}
} // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment