From 0e9921b75a0fdd639a504ec8443fc1fe801becd7 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 1 Sep 2020 09:39:43 -0700 Subject: Export of internal Abseil changes -- 23500704dd7c2642fad49f88c07ce41ebaab12e4 by Abseil Team : 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 : Record rehash count in hashtablez This will help identify which containers could benefit from a reserve call. PiperOrigin-RevId: 329510552 -- e327e54b805d67556f934fa7f7dc2d4e72fa066a by Abseil Team : 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 : Release absl::StatusOr. PiperOrigin-RevId: 329353348 -- bf4d2a7f8b089e2adf14d32b0e39de0a981005c3 by Xiaoyi Zhang : Internal change PiperOrigin-RevId: 329337031 -- 42fa7d2fb993bbfc344954227cf1eeb801eca065 by Abseil Team : Internal change PiperOrigin-RevId: 329099807 GitOrigin-RevId: 23500704dd7c2642fad49f88c07ce41ebaab12e4 Change-Id: I6713e4ca3bb0ab2ced5e487827ae036ab8ac61f1 --- absl/status/statusor.h | 670 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 670 insertions(+) create mode 100644 absl/status/statusor.h (limited to 'absl/status/statusor.h') diff --git a/absl/status/statusor.h b/absl/status/statusor.h new file mode 100644 index 00000000..95f99f4d --- /dev/null +++ b/absl/status/statusor.h @@ -0,0 +1,670 @@ +// 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 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 does not allow its Status +// value to be absl::OkStatus(). +// +// The primary use-case for StatusOr is as the return value of a +// function which may fail. +// +// Example usage of a StatusOr: +// +// StatusOr 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 result = DoBigCalculationThatCouldFail(); +// const Foo& foo = result.value(); +// foo.DoSomethingCool(); +// +// Example usage of a StatusOr>: +// +// StatusOr> 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: +// +// StatusOr FooFactory::MakeFoo(int arg) { +// if (arg <= 0) { +// return absl::Status(absl::StatusCode::kInvalidArgument, +// "Arg must be positive"); +// } +// return Foo(arg); +// } +// +// NULL POINTERS +// +// Historically StatusOr treated null pointers specially. This is no longer +// true -- a StatusOr 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 +#include +#include +#include +#include +#include + +#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 +class ABSL_MUST_USE_RESULT StatusOr; + +template +class StatusOr : private internal_statusor::StatusOrData, + private internal_statusor::CopyCtorBase, + private internal_statusor::MoveCtorBase, + private internal_statusor::CopyAssignBase, + private internal_statusor::MoveAssignBase { + template + friend class StatusOr; + + typedef internal_statusor::StatusOrData 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> will be initialized with an empty vector, + // instead of a Status::UNKNOWN status. + explicit StatusOr(); + + // StatusOr is copy constructible if T is copy constructible. + StatusOr(const StatusOr&) = default; + // StatusOr is copy assignable if T is copy constructible and copy + // assignable. + StatusOr& operator=(const StatusOr&) = default; + + // StatusOr is move constructible if T is move constructible. + StatusOr(StatusOr&&) = default; + // StatusOr is moveAssignable if T is move constructible and move + // assignable. + StatusOr& operator=(StatusOr&&) = default; + + // Converting constructors from StatusOr, when T is constructible from U. + // To avoid ambiguity, they are disabled if T is also constructible from + // StatusOr. Explicit iff the corresponding construction of T from U is + // explicit. + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + std::is_convertible, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr(const StatusOr& other) // NOLINT + : Base(static_cast::Base&>(other)) {} + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + absl::negation>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + explicit StatusOr(const StatusOr& other) + : Base(static_cast::Base&>(other)) {} + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, std::is_constructible, + std::is_convertible, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr(StatusOr&& other) // NOLINT + : Base(static_cast::Base&&>(other)) {} + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, std::is_constructible, + absl::negation>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + explicit StatusOr(StatusOr&& other) + : Base(static_cast::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. + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + std::is_assignable, + absl::negation< + internal_statusor:: + IsConstructibleOrConvertibleOrAssignableFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr& operator=(const StatusOr& other) { + this->Assign(other); + return *this; + } + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, std::is_constructible, + std::is_assignable, + absl::negation< + internal_statusor:: + IsConstructibleOrConvertibleOrAssignableFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr& operator=(StatusOr&& 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 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`. + // + // REQUIRES: !Status(std::forward(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, + std::is_constructible, + absl::negation, absl::StatusOr>>, + absl::negation, T>>, + absl::negation, absl::in_place_t>>, + absl::negation>>::value, + int> = 0> + StatusOr(U&& v) : Base(std::forward(v)) {} + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + absl::negation, absl::StatusOr>>, + absl::negation, T>>, + absl::negation, absl::in_place_t>>, + absl::negation>>::value, + int> = 0> + explicit StatusOr(U&& v) : Base(std::forward(v)) {} + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + std::is_convertible, + std::is_constructible, + absl::negation, absl::StatusOr>>, + absl::negation, T>>, + absl::negation, absl::in_place_t>>, + absl::negation>>::value, + int> = 0> + StatusOr& operator=(U&& v) { + this->AssignStatus(std::forward(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(v)`; Otherwise, it is directly-initialized + // from `std::forward(v)`. + // This function does not participate in overload unless: + // 1. `std::is_constructible_v` is true, + // 2. `std::is_assignable_v` is true. + // 3. `std::is_same_v, std::remove_cvref_t>` is false. + // 4. Assigning `U` to `T` is not ambiguous: + // If `U` is `StatusOr` and `T` is constructible and assignable from + // both `StatusOr` and `V`, the assignment is considered bug-prone and + // ambiguous thus will fail to compile. For example: + // StatusOr s1 = true; // s1.ok() && *s1 == true + // StatusOr s2 = false; // s2.ok() && *s2 == false + // s1 = s2; // ambiguous, `s1 = *s2` or `s1 = bool(s2)`? + template < + typename U = T, + typename = typename std::enable_if, std::is_assignable, + absl::disjunction< + std::is_same>, T>, + absl::conjunction< + absl::negation>, + absl::negation>>>, + internal_statusor::IsForwardingAssignmentValid>::value>::type> + StatusOr& operator=(U&& v) { + static_assert( + !absl::conjunction< + std::is_constructible, std::is_assignable, + std::is_constructible, + std::is_assignable, + absl::negation>>>>::value, + "U can assign to both T and Status, will result in semantic change"); + static_assert( + !absl::conjunction< + std::is_constructible, std::is_assignable, + internal_statusor::HasConversionOperatorToStatusOr, + absl::negation>>>>::value, + "U can assign to T and convert to StatusOr, will result in semantic " + "change"); + this->Assign(std::forward(v)); + return *this; + } + + // Constructs the inner value T in-place using the provided args, using the + // T(args...) constructor. + template + explicit StatusOr(absl::in_place_t, Args&&... args); + template + explicit StatusOr(absl::in_place_t, std::initializer_list 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, where J is convertible to T. + template < + typename U = T, + absl::enable_if_t< + absl::conjunction< + internal_statusor::IsDirectInitializationValid, + std::is_constructible, std::is_convertible, + absl::disjunction< + std::is_same>, + T>, + absl::conjunction< + absl::negation>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>>>::value, + int> = 0> + StatusOr(U&& u) // NOLINT + : StatusOr(absl::in_place, std::forward(u)) { + static_assert( + !absl::conjunction< + std::is_convertible, std::is_convertible, + absl::negation>>>>::value, + "U is convertible to both T and Status, will result in semantic " + "change"); + static_assert( + !absl::conjunction< + std::is_convertible, + internal_statusor::HasConversionOperatorToStatusOr, + absl::negation>>>>::value, + "U can construct T and convert to StatusOr, will result in semantic " + "change"); + } + + template < + typename U = T, + absl::enable_if_t< + absl::conjunction< + internal_statusor::IsDirectInitializationValid, + absl::disjunction< + std::is_same>, + T>, + absl::conjunction< + absl::negation>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>>, + std::is_constructible, + absl::negation>>::value, + int> = 0> + explicit StatusOr(U&& u) // NOLINT + : StatusOr(absl::in_place, std::forward(u)) { + static_assert( + !absl::conjunction< + std::is_constructible, + std::is_constructible, + absl::negation>>>>::value, + "U can construct both T and Status, will result in semantic " + "change"); + static_assert( + !absl::conjunction< + std::is_constructible, + internal_statusor::HasConversionOperatorToStatusOr, + absl::negation>>>>::value, + "U can construct T and convert to StatusOr, 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 + T value_or(U&& default_value) const&; + template + 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 + T& emplace(Args&&... args) { + if (ok()) { + this->Clear(); + this->MakeValue(std::forward(args)...); + } else { + this->MakeValue(std::forward(args)...); + this->status_ = absl::OkStatus(); + } + return this->data_; + } + + template < + typename U, typename... Args, + absl::enable_if_t< + std::is_constructible&, Args&&...>::value, + int> = 0> + T& emplace(std::initializer_list ilist, Args&&... args) { + if (ok()) { + this->Clear(); + this->MakeValue(ilist, std::forward(args)...); + } else { + this->MakeValue(ilist, std::forward(args)...); + this->status_ = absl::OkStatus(); + } + return this->data_; + } + + private: + using internal_statusor::StatusOrData::Assign; + template + void Assign(const absl::StatusOr& other); + template + void Assign(absl::StatusOr&& other); +}; + +template +bool operator==(const StatusOr& lhs, const StatusOr& rhs) { + if (lhs.ok() && rhs.ok()) return *lhs == *rhs; + return lhs.status() == rhs.status(); +} + +template +bool operator!=(const StatusOr& lhs, const StatusOr& rhs) { + return !(lhs == rhs); +} + +//////////////////////////////////////////////////////////////////////////////// +// Implementation details for StatusOr + +// TODO(sbenza): avoid the string here completely. +template +StatusOr::StatusOr() : Base(Status(absl::StatusCode::kUnknown, "")) {} + +template +template +inline void StatusOr::Assign(const StatusOr& other) { + if (other.ok()) { + this->Assign(*other); + } else { + this->AssignStatus(other.status()); + } +} + +template +template +inline void StatusOr::Assign(StatusOr&& other) { + if (other.ok()) { + this->Assign(*std::move(other)); + } else { + this->AssignStatus(std::move(other).status()); + } +} +template +template +StatusOr::StatusOr(absl::in_place_t, Args&&... args) + : Base(absl::in_place, std::forward(args)...) {} + +template +template +StatusOr::StatusOr(absl::in_place_t, std::initializer_list ilist, + Args&&... args) + : Base(absl::in_place, ilist, std::forward(args)...) {} + +template +const Status& StatusOr::status() const & { return this->status_; } +template +Status StatusOr::status() && { + return ok() ? OkStatus() : std::move(this->status_); +} + +template +const T& StatusOr::value() const& { + if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_); + return this->data_; +} + +template +T& StatusOr::value() & { + if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_); + return this->data_; +} + +template +const T&& StatusOr::value() const&& { + if (!this->ok()) { + internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_)); + } + return std::move(this->data_); +} + +template +T&& StatusOr::value() && { + if (!this->ok()) { + internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_)); + } + return std::move(this->data_); +} + +template +const T& StatusOr::operator*() const& { + this->EnsureOk(); + return this->data_; +} + +template +T& StatusOr::operator*() & { + this->EnsureOk(); + return this->data_; +} + +template +const T&& StatusOr::operator*() const&& { + this->EnsureOk(); + return std::move(this->data_); +} + +template +T&& StatusOr::operator*() && { + this->EnsureOk(); + return std::move(this->data_); +} + +template +const T* StatusOr::operator->() const { + this->EnsureOk(); + return &this->data_; +} + +template +T* StatusOr::operator->() { + this->EnsureOk(); + return &this->data_; +} + +template +template +T StatusOr::value_or(U&& default_value) const& { + if (ok()) { + return this->data_; + } + return std::forward(default_value); +} + +template +template +T StatusOr::value_or(U&& default_value) && { + if (ok()) { + return std::move(this->data_); + } + return std::forward(default_value); +} + +template +void StatusOr::IgnoreError() const { + // no-op +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_STATUSOR_H_ -- cgit v1.2.3