diff options
author | Kane York <kanepyork@gmail.com> | 2020-07-18T23·23-0700 |
---|---|---|
committer | Kane York <rikingcoding@gmail.com> | 2020-07-19T00·51+0000 |
commit | 9f5f71616affc7c3c588bfb97d578bfc61839b37 (patch) | |
tree | 36dcd97947d8669e65a64202c9bd96fc439ab2f5 /third_party/abseil_cpp/absl/status/statusor.h | |
parent | bd02cae03244cbeff1e90278ee0e8c4e2f5f4d0d (diff) |
feat(3p/absl): import StatusOr from tensorflow r/1391
Additionally, add tests for the macros. A future CL will enable the tests in CI. Change-Id: Id4445a1aa65bf6751b87606f37654f3fc6d20efc Tested-By: kanepyork <rikingcoding@gmail.com> Reviewed-on: https://cl.tvl.fyi/c/depot/+/1274 Reviewed-by: tazjin <mail@tazj.in> Reviewed-by: glittershark <grfn@gws.fyi> Tested-by: BuildkiteCI
Diffstat (limited to 'third_party/abseil_cpp/absl/status/statusor.h')
-rw-r--r-- | third_party/abseil_cpp/absl/status/statusor.h | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/third_party/abseil_cpp/absl/status/statusor.h b/third_party/abseil_cpp/absl/status/statusor.h new file mode 100644 index 000000000000..59a52cb782b4 --- /dev/null +++ b/third_party/abseil_cpp/absl/status/statusor.h @@ -0,0 +1,394 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +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 + + http://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 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 StatusCode::kOk. +// +// 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 absl::InvalidArgumentError("Arg must be positive"); +// } else { +// return new Foo(arg); +// } +// } +// +// Note that the assignment operators require that destroying the currently +// stored value cannot invalidate the argument; in other words, the argument +// cannot be an alias for the current value, or anything owned by the current +// value. +#ifndef ABSL_STATUS_STATUSOR_H_ +#define ABSL_STATUS_STATUSOR_H_ + +#include "absl/status/status.h" +#include "absl/status/statusor_internals.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +template <typename T> +class StatusOr : private internal_statusor::StatusOrData<T>, + private internal_statusor::TraitsBase< + std::is_copy_constructible<T>::value, + std::is_move_constructible<T>::value> { + template <typename U> + friend class StatusOr; + + typedef internal_statusor::StatusOrData<T> Base; + + public: + typedef T element_type; // DEPRECATED: use `value_type`. + 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 + // StatusOr<std::vector<int>> will be initialized with an empty vector, + // instead of a Status::UNKNOWN status. + explicit StatusOr(); + + // StatusOr<T> will be copy constructible/assignable if T is copy + // constructible. + StatusOr(const StatusOr&) = default; + StatusOr& operator=(const StatusOr&) = default; + + // StatusOr<T> will be move constructible/assignable if T is move + // constructible. + StatusOr(StatusOr&&) = default; + StatusOr& operator=(StatusOr&&) = default; + + // Conversion copy/move constructor, T must be convertible from U. + template <typename U, typename std::enable_if< + std::is_convertible<U, T>::value>::type* = nullptr> + StatusOr(const StatusOr<U>& other); + template <typename U, typename std::enable_if< + std::is_convertible<U, T>::value>::type* = nullptr> + StatusOr(StatusOr<U>&& other); + + // Conversion copy/move assignment operator, T must be convertible from U. + template <typename U, typename std::enable_if< + std::is_convertible<U, T>::value>::type* = nullptr> + StatusOr& operator=(const StatusOr<U>& other); + template <typename U, typename std::enable_if< + std::is_convertible<U, T>::value>::type* = nullptr> + StatusOr& operator=(StatusOr<U>&& other); + + // Constructs a new StatusOr with the given value. After calling this + // constructor, calls to ValueOrDie() will succeed, and calls to status() will + // return OK. + // + // NOTE: Not explicit - we want to use StatusOr<T> as a return type + // so it is convenient and sensible to be able to do 'return T()' + // when the return type is StatusOr<T>. + // + // REQUIRES: T is copy constructible. + StatusOr(const T& value); + + // Constructs a new StatusOr with the given non-ok status. After calling + // this constructor, calls to ValueOrDie() will CHECK-fail. + // + // 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()' when the return type is StatusOr<T>. + // + // REQUIRES: !status.ok(). This requirement is enforced with either an + // exception (the passed absl::Status) or a FATAL log. + StatusOr(const Status& status); + StatusOr& operator=(const Status& status); + + // TODO(b/62186997): Add operator=(T) overloads. + + // Similar to the `const T&` overload. + // + // REQUIRES: T is move constructible. + StatusOr(T&& value); + + // RValue versions of the operations declared above. + StatusOr(Status&& status); + StatusOr& operator=(Status&& status); + + // Returns this->status().ok() + bool ok() const { return this->status_.ok(); } + + // Returns a reference to our status. If this contains a T, then + // returns OkStatus(). + const Status& status() const &; + Status status() &&; + + // Returns a reference to our current value, or CHECK-fails if !this->ok(). + // + // Note: for value types that are cheap to copy, prefer simple code: + // + // T value = statusor.ValueOrDie(); + // + // Otherwise, if the value type is expensive to copy, but can be left + // in the StatusOr, simply assign to a reference: + // + // T& value = statusor.ValueOrDie(); // or `const T&` + // + // Otherwise, if the value type supports an efficient move, it can be + // used as follows: + // + // T value = std::move(statusor).ValueOrDie(); + // + // The std::move on statusor instead of on the whole expression enables + // warnings about possible uses of the statusor object after the move. + // C++ style guide waiver for ref-qualified overloads granted in cl/143176389 + // See go/ref-qualifiers for more details on such overloads. + const T& ValueOrDie() const &; + T& ValueOrDie() &; + const T&& ValueOrDie() const &&; + T&& ValueOrDie() &&; + + // Returns a reference to the current value. + // + // REQUIRES: this->ok() == true, otherwise the behavior is undefined. + // + // Use this->ok() or `operator bool()` to verify that there is a current + // value. Alternatively, see ValueOrDie() 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() or `operator bool()` to verify that there is a current + // value. + const T* operator->() const; + T* operator->(); + + T ConsumeValueOrDie() { return std::move(ValueOrDie()); } + + // 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; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Implementation details for StatusOr<T> + +template <typename T> +StatusOr<T>::StatusOr() : Base(Status(StatusCode::kUnknown, "")) {} + +template <typename T> +StatusOr<T>::StatusOr(const T& value) : Base(value) {} + +template <typename T> +StatusOr<T>::StatusOr(const Status& status) : Base(status) {} + +template <typename T> +StatusOr<T>& StatusOr<T>::operator=(const Status& status) { + this->Assign(status); + return *this; +} + +template <typename T> +StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {} + +template <typename T> +StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {} + +template <typename T> +StatusOr<T>& StatusOr<T>::operator=(Status&& status) { + this->Assign(std::move(status)); + return *this; +} + +template <typename T> +template <typename U, + typename std::enable_if<std::is_convertible<U, T>::value>::type*> +inline StatusOr<T>::StatusOr(const StatusOr<U>& other) + : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} + +template <typename T> +template <typename U, + typename std::enable_if<std::is_convertible<U, T>::value>::type*> +inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) { + if (other.ok()) + this->Assign(other.ValueOrDie()); + else + this->Assign(other.status()); + return *this; +} + +template <typename T> +template <typename U, + typename std::enable_if<std::is_convertible<U, T>::value>::type*> +inline StatusOr<T>::StatusOr(StatusOr<U>&& other) + : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} + +template <typename T> +template <typename U, + typename std::enable_if<std::is_convertible<U, T>::value>::type*> +inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) { + if (other.ok()) { + this->Assign(std::move(other).ValueOrDie()); + } else { + this->Assign(std::move(other).status()); + } + return *this; +} + +template <typename T> +const Status& StatusOr<T>::status() const & { + return this->status_; +} +template <typename T> +Status StatusOr<T>::status() && { + // Note that we copy instead of moving the status here so that + // ~StatusOrData() can call ok() without invoking UB. + return ok() ? OkStatus() : this->status_; +} + +template <typename T> +const T& StatusOr<T>::ValueOrDie() const & { + this->EnsureOk(); + return this->data_; +} + +template <typename T> +T& StatusOr<T>::ValueOrDie() & { + this->EnsureOk(); + return this->data_; +} + +template <typename T> +const T&& StatusOr<T>::ValueOrDie() const && { + this->EnsureOk(); + return std::move(this->data_); +} + +template <typename T> +T&& StatusOr<T>::ValueOrDie() && { + 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> +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> +void StatusOr<T>::IgnoreError() const { + // no-op +} + +ABSL_NAMESPACE_END +} // namespace absl + +#define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ + ABSL_ASSERT_OK_AND_ASSIGN_IMPL( \ + ABSL_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \ + rexpr); + +#define ABSL_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \ + auto statusor = (rexpr); \ + ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \ + lhs = std::move(statusor.ValueOrDie()) + +#define ABSL_STATUS_MACROS_CONCAT_NAME(x, y) ABSL_STATUS_MACROS_CONCAT_IMPL(x, y) +#define ABSL_STATUS_MACROS_CONCAT_IMPL(x, y) x##y + +#define ASSIGN_OR_RETURN(lhs, rexpr) \ + ABSL_ASSIGN_OR_RETURN_IMPL( \ + ABSL_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr) + +#define ABSL_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \ + auto statusor = (rexpr); \ + if (ABSL_PREDICT_FALSE(!statusor.ok())) { \ + return statusor.status(); \ + } \ + lhs = std::move(statusor.ValueOrDie()) + +#define RETURN_IF_ERROR(status) \ + do { \ + if (ABSL_PREDICT_FALSE(!status.ok())) { \ + return status; \ + } \ + } while(0) + +#endif // ABSL_STATUS_STATUSOR_H_ |