diff options
author | Abseil Team <absl-team@google.com> | 2020-09-03 12:54:45 -0700 |
---|---|---|
committer | Derek Mauro <dmauro@google.com> | 2020-09-04 12:59:29 -0400 |
commit | 7ba8cdb56df3bf4fe4ab4606f3fe4b2ab825afac (patch) | |
tree | 058d9db0e1acfd979929ab48e9bfe9e802c94741 /absl/status | |
parent | 930fbec75b452af8bb8c796f5bb754e953e29cf5 (diff) |
Export of internal Abseil changes
--
8ae3cb636b81b6498ef30fbe59cc102a4098cad2 by Tom Manshreck <shreck@google.com>:
Upgrade absl::StatusOr<T> docs to Abseil standards
PiperOrigin-RevId: 329975341
--
7c7baf586c7380497a751f1a186a4ef0e650161a by Tom Manshreck <shreck@google.com>:
Update absl::Status to Abseil documentation standards
PiperOrigin-RevId: 329942967
--
6710d022ba987dbae7a5d83045a6697afeb972c4 by Derek Mauro <dmauro@google.com>:
Internal change
PiperOrigin-RevId: 329775111
--
fd182dc699e18522ef16d269731c57669c5487d2 by Abseil Team <absl-team@google.com>:
Google-internal change.
PiperOrigin-RevId: 329737362
--
c0df5e27ffb48d9c784e7019267580fcb4a3f92e by Xiaoyi Zhang <zhangxy@google.com>:
Remove obsolete `static_assert`.
PiperOrigin-RevId: 329727604
GitOrigin-RevId: 8ae3cb636b81b6498ef30fbe59cc102a4098cad2
Change-Id: Ic9dede0ab97f799e7f4093fae75ae0ec6cc21437
Diffstat (limited to 'absl/status')
-rw-r--r-- | absl/status/status.h | 227 | ||||
-rw-r--r-- | absl/status/statusor.h | 368 |
2 files changed, 394 insertions, 201 deletions
diff --git a/absl/status/status.h b/absl/status/status.h index 8e1974c9..530f5807 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -24,10 +24,11 @@ // * A set of helper functions for creating status codes and checking their // values // -// Within Google, `absl::Status` is the primary mechanism for indicating -// recoverable errors across API boundaries (and in particular across RPC -// boundaries). Most functions which can produce a recoverable error should -// be designed to return an `absl::Status` (or `absl::StatusOr`). +// Within Google, `absl::Status` is the primary mechanism for gracefully +// handling errors across API boundaries (and in particular across RPC +// boundaries). Some of these errors may be recoverable, but others may not. +// Most functions which can produce a recoverable error should be designed to +// return an `absl::Status` (or `absl::StatusOr`). // // Example: // @@ -47,16 +48,6 @@ // error codes (of type `absl::StatusCode`) enumerated in this header file. // These canonical codes are understood across the codebase and will be // accepted across all API and RPC boundaries. -// -// An `absl::Status` can optionally include a payload with more information -// about the error. Typically, this payload serves one of several purposes: -// -// * It may provide more fine-grained semantic information about the error to -// facilitate actionable remedies. -// * It may provide human-readable contexual information that is more -// appropriate -// to display to an end user. -// #ifndef ABSL_STATUS_STATUS_H_ #define ABSL_STATUS_STATUS_H_ @@ -290,18 +281,99 @@ std::ostream& operator<<(std::ostream& os, StatusCode code); // absl::Status // +// The `absl::Status` class is generally used to gracefully handle errors +// across API boundaries (and in particular across RPC boundaries). Some of +// these errors may be recoverable, but others may not. Most +// functions which can produce a recoverable error should be designed to return +// either an `absl::Status` (or the similar `absl::StatusOr<T>`, which holds +// either an object of type `T` or an error). +// +// API developers should construct their functions to return `absl::OkStatus()` +// upon success, or an `absl::StatusCode` upon another type of error (e.g +// an `absl::StatusCode::kInvalidArgument` error). The API provides convenience +// functions to constuct each status code. +// +// Example: +// +// absl::Status myFunction(absl::string_view fname, ...) { +// ... +// // encounter error +// if (error condition) { +// // Construct an absl::StatusCode::kInvalidArgument error +// return absl::InvalidArgumentError("bad mode"); +// } +// // else, return OK +// return absl::OkStatus(); +// } +// +// Users handling status error codes should prefer checking for an OK status +// using the `ok()` member function. Handling multiple error codes may justify +// use of switch statement, but only check for error codes you know how to +// handle; do not try to exhaustively match against all canonical error codes. +// Errors that cannot be handled should be logged and/or propagated for higher +// levels to deal with. If you do use a switch statement, make sure that you +// also provide a `default:` switch case, so that code does not break as other +// canonical codes are added to the API. +// +// Example: +// +// absl::Status result = DoSomething(); +// if (!result.ok()) { +// LOG(ERROR) << result; +// } +// +// // Provide a default if switching on multiple error codes +// switch (result.code()) { +// // The user hasn't authenticated. Ask them to reauth +// case absl::StatusCode::kUnauthenticated: +// DoReAuth(); +// break; +// // The user does not have permission. Log an error. +// case absl::StatusCode::kPermissionDenied: +// LOG(ERROR) << result; +// break; +// // Propogate the error otherwise +// default: +// return true; +// } +// +// An `absl::Status` can optionally include a payload with more information +// about the error. Typically, this payload serves one of several purposes: +// +// * It may provide more fine-grained semantic information about the error to +// facilitate actionable remedies. +// * It may provide human-readable contexual information that is more +// appropriate to display to an end user. +// +// Example: +// +// absl::Status result = DoSomething(); +// // Inform user to retry after 30 seconds +// // See more error details in googleapis/google/rpc/error_details.proto +// if (absl::IsResourceExhausted(result)) { +// google::rpc::RetryInfo info; +// info.retry_delay().seconds() = 30; +// // Payloads require a unique key (a URL to ensure no collisions with +// // other payloads), and an `absl::Cord` to hold the encoded data. +// absl::string_view url = "type.googleapis.com/google.rpc.RetryInfo"; +// result.SetPayload(url, info.SerializeAsCord()); +// return result; +// } +// class ABSL_MUST_USE_RESULT Status final { public: // Constructors - // Creates an OK status with no message or payload. + // This default constructor creates an OK status with no message or payload. + // Avoid this constructor and pefer explicit construction of an OK status with + // `absl::OkStatus()`. Status(); - // Create a status in the canonical error space with the specified code and - // error message. If `code == absl::StatusCode::kOk`, `msg` is ignored and an - // object identical to an OK status is constructed. + // Creates a status in the canonical error space with the specified + // `absl::StatusCode` and error message. If `code == absl::StatusCode::kOk`, + // `msg` is ignored and an object identical to an OK status is constructed. // - // `msg` must be in UTF-8. The implementation may complain (e.g., + // The `msg` string must be in UTF-8. The implementation may complain (e.g., // by printing a warning) if it is not. Status(absl::StatusCode code, absl::string_view msg); @@ -318,42 +390,51 @@ class ABSL_MUST_USE_RESULT Status final { // Status::Update() // - // If `this->ok()`, stores `new_status` into *this. If `!this->ok()`, - // preserves the current data. May, in the future, augment the current status - // with additional information about `new_status`. + // Updates the existing status with `new_status` provided that `this->ok()`. + // If the existing status already contains a non-OK error, this update has no + // effect and preserves the current data. Note that this behavior may change + // in the future to augment a current non-ok status with additional + // information about `new_status`. + // + // `Update()` provides a convenient way of keeping track of the first error + // encountered. // - // Convenient way of keeping track of the first error encountered. - // Instead of: - // if (overall_status.ok()) overall_status = new_status - // Use: + // Example: + // // Instead of "if (overall_status.ok()) overall_status = new_status" // overall_status.Update(new_status); // - // Style guide exception for rvalue reference granted in CL 153567220. void Update(const Status& new_status); void Update(Status&& new_status); // Status::ok() // - // Returns true if the Status is OK. + // Returns `true` if `this->ok()`. Prefer checking for an OK status using this + // member function. ABSL_MUST_USE_RESULT bool ok() const; // Status::code() // - // Returns the (canonical) error code. + // Returns the canonical error code of type `absl::StatusCode` of this status. absl::StatusCode code() const; // Status::raw_code() // - // Returns the raw (canonical) error code which could be out of the range of - // the local `absl::StatusCode` enum. NOTE: This should only be called when - // converting to wire format. Use `code` for error handling. + // Returns a raw (canonical) error code corresponding to the enum value of + // `google.rpc.Code` definitions within + // https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto. + // These values could be out of the range of canonical `absl::StatusCode` + // enum values. + // + // NOTE: This function should only be called when converting to an associated + // wire format. Use `Status::code()` for error handling. int raw_code() const; // Status::message() // - // Returns the error message. Note: prefer ToString() for debug logging. - // This message rarely describes the error code. It is not unusual for the - // error message to be the empty string. + // Returns the error message associated with this error code, if available. + // Note that this message rarely describes the error code. It is not unusual + // for the error message to be the empty string. As a result, prefer + // `Status::ToString()` for debug logging. absl::string_view message() const; friend bool operator==(const Status&, const Status&); @@ -361,12 +442,14 @@ class ABSL_MUST_USE_RESULT Status final { // Status::ToString() // - // Returns a combination of the error code name, the message and the payloads. - // You can expect the code name and the message to be substrings of the - // result, and the payloads to be printed by the registered printer extensions - // if they are recognized. - // WARNING: Do not depend on the exact format of the result of `ToString()` - // which is subject to change. + // Returns a combination of the error code name, the message and any + // associated payload messages. This string is designed simply to be human + // readable and its exact format should not be load bearing. Do not depend on + // the exact format of the result of `ToString()` which is subject to change. + // + // The printed code name and the message are generally substrings of the + // result, and the payloads to be printed use the status payload printer + // mechanism (which is internal). std::string ToString() const; // Status::IgnoreError() @@ -378,52 +461,71 @@ class ABSL_MUST_USE_RESULT Status final { // swap() // - // Swap the contents of `a` with `b` + // Swap the contents of one status with another. friend void swap(Status& a, Status& b); //---------------------------------------------------------------------------- - // Payload management APIs + // Payload Management APIs //---------------------------------------------------------------------------- - // Type URL should be unique and follow the naming convention below: - // The idea of type URL comes from `google.protobuf.Any` - // (https://developers.google.com/protocol-buffers/docs/proto3#any). The - // type URL should be globally unique and follow the format of URL - // (https://en.wikipedia.org/wiki/URL). The default type URL for a given - // protobuf message type is "type.googleapis.com/packagename.messagename". For - // other custom wire formats, users should define the format of type URL in a - // similar practice so as to minimize the chance of conflict between type - // URLs. Users should make sure that the type URL can be mapped to a concrete + // A payload may be attached to a status to provide additional context to an + // error that may not be satisifed by an existing `absl::StatusCode`. + // Typically, this payload serves one of several purposes: + // + // * It may provide more fine-grained semantic information about the error + // to facilitate actionable remedies. + // * It may provide human-readable contexual information that is more + // appropriate to display to an end user. + // + // A payload consists of a [key,value] pair, where the key is a string + // referring to a unique "type URL" and the value is an object of type + // `absl::Cord` to hold the contextual data. + // + // The "type URL" should be unique and follow the format of a URL + // (https://en.wikipedia.org/wiki/URL) and, ideally, provide some + // documentation or schema on how to interpret its associated data. For + // example, the default type URL for a protobuf message type is + // "type.googleapis.com/packagename.messagename". Other custom wire formats + // should define the format of type URL in a similar practice so as to + // minimize the chance of conflict between type URLs. + // Users should ensure that the type URL can be mapped to a concrete // C++ type if they want to deserialize the payload and read it effectively. + // + // To attach a payload to a status object, call `Status::SetPayload()`, + // passing it the type URL and an `absl::Cord` of associated data. Similarly, + // to extract the payload from a status, call `Status::GetPayload()`. You + // may attach multiple payloads (with differing type URLs) to any given + // status object, provided that the status is currently exhibiting an error + // code (i.e. is not OK). // Status::GetPayload() // - // Gets the payload based for `type_url` key, if it is present. + // Gets the payload of a status given its unique `type_url` key, if present. absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const; // Status::SetPayload() // - // Sets the payload for `type_url` key for a non-ok status, overwriting any - // existing payload for `type_url`. + // Sets the payload for a non-ok status using a `type_url` key, overwriting + // any existing payload for that `type_url`. // - // NOTE: Does nothing if the Status is ok. + // NOTE: This function does nothing if the Status is ok. void SetPayload(absl::string_view type_url, absl::Cord payload); // Status::ErasePayload() // - // Erases the payload corresponding to the `type_url` key. Returns true if + // Erases the payload corresponding to the `type_url` key. Returns `true` if // the payload was present. bool ErasePayload(absl::string_view type_url); // Status::ForEachPayload() // - // Iterates over the stored payloads and calls `visitor(type_key, payload)` - // for each one. + // Iterates over the stored payloads and calls the + // `visitor(type_key, payload)` callable for each one. // - // NOTE: The order of calls to `visitor` is not specified and may change at + // NOTE: The order of calls to `visitor()` is not specified and may change at // any time. // - // NOTE: Any mutation on the same 'Status' object during visitation is + // NOTE: Any mutation on the same 'absl::Status' object during visitation is // forbidden and could result in undefined behavior. void ForEachPayload( const std::function<void(absl::string_view, const absl::Cord&)>& visitor) @@ -494,7 +596,8 @@ class ABSL_MUST_USE_RESULT Status final { // OkStatus() // -// Returns an OK status, equivalent to a default constructed instance. +// Returns an OK status, equivalent to a default constructed instance. Prefer +// usage of `absl::OkStatus()` when constructing such an OK status. Status OkStatus(); // operator<<() diff --git a/absl/status/statusor.h b/absl/status/statusor.h index 95f99f4d..ff0dab65 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -12,58 +12,27 @@ // 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(). +// ----------------------------------------------------------------------------- +// File: statusor.h +// ----------------------------------------------------------------------------- // -// The primary use-case for StatusOr<T> is as the return value of a -// function which may fail. +// An `absl::StatusOr<T>` represents a union of an `absl::Status` object +// and an object of type `T`. The `absl::StatusOr<T>` will either contain an +// object of type `T` (indicating a successful operation), or an error (of type +// `absl::Status`) explaining why such a value is not present. // -// Example usage of a StatusOr<T>: +// In general, check the success of an operation returning an +// `absl::StatusOr<T>` like you would an `absl::Status` by using the `ok()` +// member function. // -// 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: // -// 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. - +// StatusOr<Foo> result = Calculation(); +// if (result.ok()) { +// result->DoSomethingCool(); +// } else { +// LOG(ERROR) << result.status(); +// } #ifndef ABSL_STATUS_STATUSOR_H_ #define ABSL_STATUS_STATUSOR_H_ @@ -83,11 +52,42 @@ namespace absl { ABSL_NAMESPACE_BEGIN + +// BadStatusOrAccess +// +// This class defines the type of object to throw (if exceptions are enabled), +// when accessing the value of an `absl::StatusOr<T>` object that does not +// contain a value. This behavior is analogous to that of +// `std::bad_optional_access` in the case of accessing an invalid +// `std::optional` value. +// +// Example: +// +// try { +// absl::StatusOr<int> v = FetchInt(); +// DoWork(v.value()); // Accessing value() when not "OK" may throw +// } catch (absl::BadStatusOrAccess& ex) { +// LOG(ERROR) << ex.status(); +// } class BadStatusOrAccess : public std::exception { public: explicit BadStatusOrAccess(absl::Status status); ~BadStatusOrAccess() override; + + // BadStatusOrAccess::what() + // + // Returns the associated explanatory string of the `absl::StatusOr<T>` + // object's error code. This function only returns the string literal "Bad + // StatusOr Access" for cases when evaluating general exceptions. + // + // The pointer of this string is guaranteed to be valid until any non-const + // function is invoked on the exception object. const char* what() const noexcept override; + + // BadStatusOrAccess::status() + // + // Returns the associated `absl::Status` of the `absl::StatusOr<T>` object's + // error. const absl::Status& status() const; private: @@ -98,6 +98,75 @@ class BadStatusOrAccess : public std::exception { template <typename T> class ABSL_MUST_USE_RESULT StatusOr; +// abls::StatusOr<T> +// +// The `absl::StatusOr<T>` class template is a union of an `absl::Status` object +// and an object of type `T`. The `absl::StatusOr<T>` models an object that is +// either a usable object, or an error (of type `absl::Status`) explaining why +// such an object is not present. An `absl::StatusOr<T>` is typically the return +// value of a function which may fail. +// +// An `absl::StatusOr<T>` can never hold an "OK" status (an +// `absl::StatusCode::kOk` value); instead, the presence of an object of type +// `T` indicates success. Instead of checking for a `kOk` value, use the +// `absl::StatusOr<T>::ok()` member function. (It is for this reason, and code +// readability, that using the `ok()` function is preferred for `absl::Status` +// as well.) +// +// Example: +// +// StatusOr<Foo> result = DoBigCalculationThatCouldFail(); +// if (result.ok()) { +// result->DoSomethingCool(); +// } else { +// LOG(ERROR) << result.status(); +// } +// +// Accessing the object held by an `absl::StatusOr<T>` should be performed via +// `operator*` or `operator->`, after a call to `ok()` confirms that the +// `absl::StatusOr<T>` holds an object of type `T`: +// +// Example: +// +// absl::StatusOr<int> i = GetCount(); +// if (foo.ok()) { +// updated_total += *i +// } +// +// NOTE: using `absl::StatusOr<T>::value()` when no valid value is present will +// throw an exception if exceptions are enabled or terminate the process when +// execeptions are not enabled. +// +// Example: +// +// StatusOr<Foo> result = DoBigCalculationThatCouldFail(); +// const Foo& foo = result.value(); // Crash/exception if no value present +// foo.DoSomethingCool(); +// +// A `absl::StatusOr<T*>` can be constructed from a null pointer like any other +// pointer value, and the result will be that `ok()` returns `true` and +// `value()` returns `nullptr`. Checking the value of pointer in an +// `absl::StatusOr<T>` generally requires a bit more care, to ensure both that a +// value is present and that value is not null: +// +// StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg); +// if (!result.ok()) { +// LOG(ERROR) << result.status(); +// } else if (*result == nullptr) { +// LOG(ERROR) << "Unexpected null pointer"; +// } else { +// (*result)->DoSomethingCool(); +// } +// +// Example factory implementation returning StatusOr<T>: +// +// StatusOr<Foo> FooFactory::MakeFoo(int arg) { +// if (arg <= 0) { +// return absl::Status(absl::StatusCode::kInvalidArgument, +// "Arg must be positive"); +// } +// return Foo(arg); +// } template <typename T> class StatusOr : private internal_statusor::StatusOrData<T>, private internal_statusor::CopyCtorBase<T>, @@ -110,30 +179,42 @@ class StatusOr : private internal_statusor::StatusOrData<T>, typedef internal_statusor::StatusOrData<T> Base; public: + // StatusOr<T>::value_type + // + // This instance data provides a generic `value_type` member for use within + // generic programming. This usage is analogous to that of + // `optional::value_type` in the case of `std::optional`. typedef T value_type; - // 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. + // Constructors + + // Constructs a new `absl::StatusOr` with an `absl::StatusCode::kUnknown` + // status. This constructor is marked 'explicit' to prevent usages in return + // values such as 'return {};', under the misconception that + // `absl::StatusOr<std::vector<int>>` will be initialized with an empty + // vector, instead of an `absl::StatusCode::kUnknown` error code. explicit StatusOr(); - // StatusOr<T> is copy constructible if T is copy constructible. + // `StatusOr<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 + // `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<T>` is move constructible if `T` is move constructible. StatusOr(StatusOr&&) = default; - // StatusOr<T> is moveAssignable if T is move constructible and move + // `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. + // Converting Constructors + + // Constructs a new `absl::StatusOr<T>` from an `absl::StatusOr<U>`, when `T` + // is constructible from `U`. To avoid ambiguity, these constructors are + // disabled if `T` is also constructible from `StatusOr<U>.`. This constructor + // is explicit if and only if the corresponding construction of `T` from `U` + // is explicit. (This constructor inherits its explicitness from the + // underlying constructor.) template < typename U, absl::enable_if_t< @@ -186,9 +267,25 @@ class StatusOr : private internal_statusor::StatusOrData<T>, 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>. + // Converting Assignment Operators + + // Creates an `absl::StatusOr<T>` through assignment from an + // `absl::StatusOr<U>` when: + // + // * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` are OK by assigning + // `U` to `T` directly. + // * `absl::StatusOr<T>` is OK and `absl::StatusOr<U>` contains an error + // code by destroying `absl::StatusOr<T>`'s value and assigning from + // `absl::StatusOr<U>' + // * `absl::StatusOr<T>` contains an error code and `absl::StatusOr<U>` is + // OK by directly initializing `T` from `U`. + // * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` contain an error + // code by assigning the `Status` in `absl::StatusOr<U>` to + // `absl::StatusOr<T>` + // + // These overloads only apply if `absl::StatusOr<T>` is constructible and + // assignable from `absl::StatusOr<U>` and `StatusOr<T>` cannot be directly + // assigned from `StatusOr<U>`. template < typename U, absl::enable_if_t< @@ -221,14 +318,13 @@ class StatusOr : private internal_statusor::StatusOrData<T>, 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`. + // Constructs a new `absl::StatusOr<T>` with a non-ok status. After calling + // this constructor, `this->ok()` will be `false` and calls to `value()` will + // crash, or produce an exception if exceptions are enabled. // - // 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>`. + // The constructor also takes any type `U` that is convertible to + // `absl::Status`. This constructor is explicit if an only if `U` is not of + // type `absl::Status` and the conversion from `U` to `Status` is explicit. // // REQUIRES: !Status(std::forward<U>(v)).ok(). This requirement is DCHECKed. // In optimized builds, passing absl::OkStatus() here will have the effect @@ -279,6 +375,7 @@ class StatusOr : private internal_statusor::StatusOrData<T>, } // 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)`. @@ -305,39 +402,25 @@ class StatusOr : private internal_statusor::StatusOrData<T>, 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. + // 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. + // Constructs the inner value `T` in-place using the provided args, using the + // `T(U)` (direct-initialization) constructor. This constructor is only valid + // if `T` can be constructed from a `U`. Can accept move or copy constructors. + // + // This constructor is explicit if `U` is not convertible to `T`. To avoid + // ambiguity, this constuctor is disabled if `U` is a `StatusOr<J>`, where `J` + // is convertible to `T`. template < typename U = T, absl::enable_if_t< @@ -355,21 +438,6 @@ class StatusOr : private internal_statusor::StatusOrData<T>, 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 < @@ -390,38 +458,42 @@ class StatusOr : private internal_statusor::StatusOrData<T>, 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() + // StatusOr<T>::ok() + // + // Returns whether or not this `absl::StatusOr<T>` holds a `T` value. This + // member function is analagous to `absl::Status::ok()` and should be used + // similarly to check the status of return values. + // + // Example: + // + // StatusOr<Foo> result = DoBigCalculationThatCouldFail(); + // if (result.ok()) { + // // Handle result + // else { + // // Handle error + // } ABSL_MUST_USE_RESULT bool ok() const { return this->status_.ok(); } - // Returns a reference to our status. If this contains a T, then - // returns absl::OkStatus(). + // StatusOr<T>::status() + // + // Returns a reference to the current `absl::Status` contained within the + // `absl::StatusOr<T>`. If `absl::StatusOr<T>` contains a `T`, then this + // function returns `absl::OkStatus()`. const Status& status() const &; Status status() &&; + // StatusOr<T>::value() + // // Returns a reference to the held value if `this->ok()`. Otherwise, throws - // `absl::BadStatusOrAccess` if exception is enabled, or `LOG(FATAL)` if - // exception is disabled. + // `absl::BadStatusOrAccess` if exceptions are enabled, or is guaranteed to + // terminate the process if exceptions are disabled. + // // If you have already checked the status using `this->ok()`, you probably // want to use `operator*()` or `operator->()` to access the value instead of // `value`. + // // Note: for value types that are cheap to copy, prefer simple code: // // T value = statusor.value(); @@ -443,28 +515,35 @@ class StatusOr : private internal_statusor::StatusOrData<T>, const T&& value() const&&; T&& value() &&; + // StatusOr<T>:: operator*() + // // Returns a reference to the current value. // - // REQUIRES: this->ok() == true, otherwise the behavior is undefined. + // 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. + // Use `this->ok()` to verify that there is a current value within the + // `absl::StatusOr<T>`. Alternatively, see the `value()` member function for a + // similar API that guarantees crashing or throwing an exception if there is + // no current value. const T& operator*() const&; T& operator*() &; const T&& operator*() const&&; T&& operator*() &&; + // StatusOr<T>::operator->() + // // Returns a pointer to the current value. // - // REQUIRES: this->ok() == true, otherwise the behavior is undefined. + // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined. // - // Use this->ok() to verify that there is a current value. + // 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`. + // StatusOr<T>::value_or() + // + // Returns the current value of `this->ok() == true`. Otherwise constructs a + // value using the provided `default_value`. // // Unlike `value`, this function returns by value, copying the current value // if necessary. If the value type supports an efficient move, it can be used @@ -472,18 +551,22 @@ class StatusOr : private internal_statusor::StatusOrData<T>, // // T value = std::move(statusor).value_or(def); // - // Unlike with `value`, calling `std::move` on the result of `value_or` will + // Unlike with `value`, calling `std::move()` on the result of `value_or` will // still trigger a copy. template <typename U> T value_or(U&& default_value) const&; template <typename U> T value_or(U&& default_value) &&; + // StatusOr<T>::IgnoreError() + // // Ignores any errors. This method does nothing except potentially suppress // complaints from any tools that are checking that errors are not dropped on // the floor. void IgnoreError() const; + // StatusOr<T>::emplace() + // // Reconstructs the inner value T in-place using the provided args, using the // T(args...) constructor. Returns reference to the reconstructed `T`. template <typename... Args> @@ -522,19 +605,26 @@ class StatusOr : private internal_statusor::StatusOrData<T>, void Assign(absl::StatusOr<U>&& other); }; +// operator==() +// +// This operator checks the equality of two `absl::StatusOr<T>` objects. template <typename T> bool operator==(const StatusOr<T>& lhs, const StatusOr<T>& rhs) { if (lhs.ok() && rhs.ok()) return *lhs == *rhs; return lhs.status() == rhs.status(); } +// operator!=() +// +// This operator checks the inequality of two `absl::StatusOr<T>` objects. template <typename T> bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) { return !(lhs == rhs); } -//////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------ // Implementation details for StatusOr<T> +//------------------------------------------------------------------------------ // TODO(sbenza): avoid the string here completely. template <typename T> |