blob: 81b97d9e42e7c03f54c0d9ea9d2ed455df4eb3f8 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// 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 Status::StatusOK(). Further, StatusOr<T*> does not allow the
// contained pointer to be nullptr.
//
// The primary use-case for StatusOr<T> is as the return value of a
// function which may fail.
//
// Example client usage for a StatusOr<T>, where T is not a pointer:
//
// StatusOr<float> result = DoBigCalculationThatCouldFail();
// if (result.ok()) {
// float answer = result.ValueOrDie();
// printf("Big calculation yielded: %f", answer);
// } else {
// LOG(ERROR) << result.status();
// }
//
// Example client usage for a StatusOr<T*>:
//
// StatusOr<Foo*> result = FooFactory::MakeNewFoo(arg);
// if (result.ok()) {
// std::unique_ptr<Foo> foo(result.ValueOrDie());
// foo->DoSomethingCool();
// } else {
// LOG(ERROR) << result.status();
// }
//
// Example client usage for a StatusOr<std::unique_ptr<T>>:
//
// StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
// if (result.ok()) {
// std::unique_ptr<Foo> foo = std::move(result.ValueOrDie());
// foo->DoSomethingCool();
// } else {
// LOG(ERROR) << result.status();
// }
//
// Example factory implementation returning StatusOr<T*>:
//
// StatusOr<Foo*> FooFactory::MakeNewFoo(int arg) {
// if (arg <= 0) {
// return Status(error::INVALID_ARGUMENT, "Arg must be positive");
// } else {
// return new Foo(arg);
// }
// }
//
#ifndef MISSIVE_UTIL_STATUSOR_H_
#define MISSIVE_UTIL_STATUSOR_H_
#include <new>
#include <string>
#include <type_traits>
#include <utility>
#include <base/compiler_specific.h>
#include <base/logging.h>
#include <base/optional.h>
#include "missive/util/status.h"
namespace reporting {
namespace internal {
// Helper class for StatusOr to use.
class StatusOrHelper {
public:
static const Status& NotInitializedStatus();
static const Status& MovedOutStatus();
static void Crash(const Status& status);
};
} // namespace internal
template <typename T>
class WARN_UNUSED_RESULT StatusOr {
template <typename U>
friend class StatusOr;
// A traits class that determines whether a type U is implicitly convertible
// from a type V. If it is convertible, then the |value| member of this class
// is statically set to true, otherwise it is statically set to false.
template <class U, typename V>
struct is_implicitly_constructible
: base::conjunction<std::is_constructible<U, V>,
std::is_convertible<V, U>> {};
public:
// Constructs a new StatusOr with UNINITIALIZED status and no value.
StatusOr() : status_(internal::StatusOrHelper::NotInitializedStatus()) {}
// Constructs a new StatusOr with the given non-ok status. After calling
// this constructor, calls to ValueOrDie() will CHECK-fail.
//
// This constructor is not declared explicit so that a function with a return
// type of |StatusOr<T>| can return a Status object, and the status will be
// implicitly converted to the appropriate return type as a matter of
// convenience.
// REQUIRES: !status.ok().
StatusOr(const Status& status) // NOLINT(runtime/explicit)
: status_(status) {
if (status.ok()) {
internal::StatusOrHelper::Crash(status);
}
}
// Constructs a StatusOr object that contains |value|. The resulting object
// is considered to have an OK status. The wrapped element can be accessed
// with ValueOrDie().
//
// This constructor is made implicit so that a function with a return type of
// |StatusOr<T>| can return an object of type |U&&|, implicitly converting
// it to a |StatusOr<T>| object.
//
// Note that |T| must be implicitly constructible from |U|, and |U| must not
// be a (cv-qualified) Status or Status-reference type. Due to C++
// reference-collapsing rules and perfect-forwarding semantics, this
// constructor matches invocations that pass |value| either as a const
// reference or as an rvalue reference. Since StatusOr needs to work for both
// reference and rvalue-reference types, the constructor uses perfect
// forwarding to avoid invalidating arguments that were passed by reference.
template <typename U,
typename E = typename std::enable_if<
is_implicitly_constructible<T, U>::value &&
!std::is_same<typename std::remove_reference<
typename std::remove_cv<U>::type>::type,
Status>::value>::type>
StatusOr(U&& value) // NOLINT(runtime/explicit)
: status_(Status::StatusOK()), value_(std::forward<U>(value)) {}
// Copy constructor.
//
// This constructor needs to be explicitly defined because the presence of
// the move-assignment operator deletes the default copy constructor. In such
// a scenario, since the deleted copy constructor has stricter binding rules
// than the templated copy constructor, the templated constructor cannot act
// as a copy constructor, and any attempt to copy-construct a |StatusOr|
// object results in a compilation error.
StatusOr(const StatusOr& other)
: status_(other.status_), value_(other.value_) {}
// Templatized constructor that constructs a |StatusOr<T>| from a const
// reference to a |StatusOr<U>|.
//
// |T| must be implicitly constructible from |const U&|.
template <typename U,
typename E = typename std::enable_if<
is_implicitly_constructible<T, const U&>::value>::type>
StatusOr(const StatusOr<U>& other) // NOLINT(runtime/explicit)
: status_(other.status_), value_(other.value_) {}
// Copy-assignment operator.
StatusOr& operator=(const StatusOr& other) {
// Check for self-assignment.
if (this == &other) {
return *this;
}
if (other.status_.ok()) {
AssignValue(other.value_);
} else {
AssignStatus(other.status_);
}
return *this;
}
// Templatized constructor which constructs a |StatusOr<T>| by moving the
// contents of a |StatusOr<U>|. |T| must be implicitly constructible from
// |U&&|.
//
// Sets |other| to contain a non-OK status with a|error::UNKNOWN|
// error code.
template <typename U,
typename E = typename std::enable_if<
is_implicitly_constructible<T, U&&>::value>::type>
StatusOr(StatusOr<U>&& other) // NOLINT(runtime/explicit)
: status_(std::move(other.status_)), value_(std::move(other.value_)) {
other.status_ = internal::StatusOrHelper::MovedOutStatus();
}
// Move-assignment operator.
//
// Sets |other| to contain a non-OK status with a |error::UNKNOWN| error
// code.
StatusOr& operator=(StatusOr&& other) {
// Check for self-assignment.
if (this == &other) {
return *this;
}
if (other.status_.ok()) {
AssignValue(std::move(other.value_.value()));
} else {
AssignStatus(std::move(other.status_));
}
other.status_ = internal::StatusOrHelper::MovedOutStatus();
return *this;
}
// Indicates whether the object contains a |T| value.
bool ok() const { return status_.ok(); }
// Gets the stored status object, or an OK status if a |T| value is stored.
Status status() const { return status_; }
// Gets the stored |T| value.
//
// This method should only be called if this StatusOr object's status is OK
// (i.e. a call to ok() returns true), otherwise this call will abort.
const T& WARN_UNUSED_RESULT ValueOrDie() const& {
if (!ok()) {
internal::StatusOrHelper::Crash(status_);
}
return value_.value();
}
// Gets a mutable reference to the stored |T| value.
//
// This method should only be called if this StatusOr object's status is OK
// (i.e. a call to ok() returns true), otherwise this call will abort.
T& WARN_UNUSED_RESULT ValueOrDie() & {
if (!ok()) {
internal::StatusOrHelper::Crash(status_);
}
return value_.value();
}
// Moves and returns the internally-stored |T| value.
//
// This method should only be called if this StatusOr object's status is OK
// (i.e. a call to ok() returns true), otherwise this call will abort. The
// StatusOr object is invalidated after this call and will be updated to
// contain a non-OK status with a |error::UNKNOWN| error code.
T WARN_UNUSED_RESULT ValueOrDie() && {
if (!ok()) {
internal::StatusOrHelper::Crash(status_);
}
// Invalidate this StatusOr object before returning control to caller.
StatusResetter set_moved_status(this,
internal::StatusOrHelper::MovedOutStatus());
return std::move(value_.value());
}
private:
class StatusResetter {
public:
StatusResetter(StatusOr<T>* status_or, const Status& reset_to_status)
: status_or_(status_or), reset_to_status_(reset_to_status) {}
StatusResetter(const StatusResetter& other) = delete;
StatusResetter& operator=(const StatusResetter& other) = delete;
~StatusResetter() {
status_or_->OverwriteValueWithStatus(reset_to_status_);
}
private:
StatusOr<T>* const status_or_;
const Status reset_to_status_;
};
// Resets |this| to contain |status|.
template <class U>
void AssignStatus(U&& status) {
if (ok()) {
OverwriteValueWithStatus(std::forward<U>(status));
} else {
status_ = std::forward<U>(status);
}
}
// Under the assumption that |this| is currently holding a value, resets the
// |value_| member and sets |status_| to indicate that |this| does not have
// a value.
template <class U>
void OverwriteValueWithStatus(U&& status) {
if (!ok()) {
LOG(FATAL) << "Object does not have a value to change from";
}
value_.reset();
status_ = std::forward<U>(status);
}
// Resets |value_| to contain the |value| and sets |status_|
// to OK, indicating that the StatusOr object has a value.
// Destroys the existing |value_|.
template <class U>
void AssignValue(U&& value) {
value_ = std::forward<U>(value);
status_ = Status::StatusOK();
}
Status status_;
base::Optional<T> value_;
};
} // namespace reporting
#endif // MISSIVE_UTIL_STATUSOR_H_