aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/stream_executor/lib
diff options
context:
space:
mode:
authorGravatar Michael Case <mikecase@google.com>2018-06-26 13:05:25 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2018-06-26 13:08:05 -0700
commit69a895b76736ebc7bd748218827ab452e9082d86 (patch)
tree02a57eb9c86a5a085421b7096f79f0f486ca4b27 /tensorflow/stream_executor/lib
parent623513f26579d04a1eb8a836b9d78896abfb599c (diff)
Moving StatusOr from XLA to stream_executor.
PiperOrigin-RevId: 202179928
Diffstat (limited to 'tensorflow/stream_executor/lib')
-rw-r--r--tensorflow/stream_executor/lib/statusor.cc40
-rw-r--r--tensorflow/stream_executor/lib/statusor.h290
-rw-r--r--tensorflow/stream_executor/lib/statusor_internals.h248
-rw-r--r--tensorflow/stream_executor/lib/statusor_test.cc676
4 files changed, 1248 insertions, 6 deletions
diff --git a/tensorflow/stream_executor/lib/statusor.cc b/tensorflow/stream_executor/lib/statusor.cc
new file mode 100644
index 0000000000..e0e851f96e
--- /dev/null
+++ b/tensorflow/stream_executor/lib/statusor.cc
@@ -0,0 +1,40 @@
+/* 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.
+==============================================================================*/
+
+#include "tensorflow/stream_executor/lib/statusor.h"
+
+#include "tensorflow/core/lib/core/errors.h"
+#include "tensorflow/core/platform/logging.h"
+
+namespace stream_executor {
+namespace port {
+namespace internal_statusor {
+
+void Helper::HandleInvalidStatusCtorArg(Status* status) {
+ const char* kMessage =
+ "An OK status is not a valid constructor argument to StatusOr<T>";
+ LOG(ERROR) << kMessage;
+ // Fall back to tensorflow::error::INTERNAL.
+ *status = ::tensorflow::errors::Internal(kMessage);
+}
+
+void Helper::Crash(const Status& status) {
+ LOG(FATAL) << "Attempting to fetch value instead of handling error "
+ << status;
+}
+
+} // namespace internal_statusor
+} // namespace port
+} // namespace stream_executor
diff --git a/tensorflow/stream_executor/lib/statusor.h b/tensorflow/stream_executor/lib/statusor.h
index dab5909674..3c716acb46 100644
--- a/tensorflow/stream_executor/lib/statusor.h
+++ b/tensorflow/stream_executor/lib/statusor.h
@@ -1,4 +1,4 @@
-/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
+/* 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.
@@ -13,19 +13,297 @@ See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
-// IWYU pragma: private, include "third_party/tensorflow/stream_executor/stream_executor.h"
-
+// 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 Status::OK.
+//
+// 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 tensorflow::InvalidArgument("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 TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_H_
#define TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_H_
-#include "tensorflow/compiler/xla/statusor.h"
+#include "tensorflow/core/platform/macros.h"
+#include "tensorflow/stream_executor/lib/status.h"
+#include "tensorflow/stream_executor/lib/statusor_internals.h"
namespace stream_executor {
namespace port {
-// Use XLA's StatusOr so we don't duplicate code.
+#if defined(__clang__)
+// Only clang supports warn_unused_result as a type annotation.
+template <typename T>
+class TF_MUST_USE_RESULT StatusOr;
+#endif
+
+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;
+
+ // 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 DCHECKed.
+ // In optimized builds, passing Status::OK() here will have the effect
+ // of passing tensorflow::error::INTERNAL as a fallback.
+ 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 Status::OK().
+ 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() &&;
+
+ 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(tensorflow::error::UNKNOWN, "")) {}
+
+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() && {
+ return ok() ? Status::OK() : std::move(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>
-using StatusOr = ::xla::StatusOr<T>;
+void StatusOr<T>::IgnoreError() const {
+ // no-op
+}
} // namespace port
} // namespace stream_executor
diff --git a/tensorflow/stream_executor/lib/statusor_internals.h b/tensorflow/stream_executor/lib/statusor_internals.h
new file mode 100644
index 0000000000..09f88f5825
--- /dev/null
+++ b/tensorflow/stream_executor/lib/statusor_internals.h
@@ -0,0 +1,248 @@
+/* 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.
+==============================================================================*/
+
+#ifndef TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_INTERNALS_H_
+#define TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_INTERNALS_H_
+
+
+#include "tensorflow/core/platform/macros.h"
+#include "tensorflow/stream_executor/lib/status.h"
+
+namespace stream_executor {
+namespace port {
+namespace internal_statusor {
+
+class Helper {
+ public:
+ // Move type-agnostic error handling to the .cc.
+ static void HandleInvalidStatusCtorArg(Status*);
+ TF_ATTRIBUTE_NORETURN static void Crash(const 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>
+ StatusOrData(const StatusOrData<U>& other) {
+ if (other.ok()) {
+ MakeValue(other.data_);
+ MakeStatus();
+ } else {
+ MakeStatus(other.status_);
+ }
+ }
+
+ template <typename U>
+ StatusOrData(StatusOrData<U>&& other) {
+ if (other.ok()) {
+ MakeValue(std::move(other.data_));
+ MakeStatus();
+ } else {
+ MakeStatus(std::move(other.status_));
+ }
+ }
+
+ explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); }
+ explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); }
+
+ explicit StatusOrData(const Status& status) : status_(status) {
+ EnsureNotOk();
+ }
+ explicit StatusOrData(Status&& status) : status_(std::move(status)) {
+ EnsureNotOk();
+ }
+
+ StatusOrData& operator=(const StatusOrData& other) {
+ if (this == &other) return *this;
+ if (other.ok())
+ Assign(other.data_);
+ else
+ Assign(other.status_);
+ return *this;
+ }
+
+ StatusOrData& operator=(StatusOrData&& other) {
+ if (this == &other) return *this;
+ if (other.ok())
+ Assign(std::move(other.data_));
+ else
+ Assign(std::move(other.status_));
+ return *this;
+ }
+
+ ~StatusOrData() {
+ if (ok()) {
+ status_.~Status();
+ data_.~T();
+ } else {
+ status_.~Status();
+ }
+ }
+
+ void Assign(const T& value) {
+ if (ok()) {
+ data_.~T();
+ MakeValue(value);
+ } else {
+ MakeValue(value);
+ status_ = Status::OK();
+ }
+ }
+
+ void Assign(T&& value) {
+ if (ok()) {
+ data_.~T();
+ MakeValue(std::move(value));
+ } else {
+ MakeValue(std::move(value));
+ status_ = Status::OK();
+ }
+ }
+
+ void Assign(const Status& status) {
+ Clear();
+ status_ = status;
+ EnsureNotOk();
+ }
+
+ void Assign(Status&& status) {
+ Clear();
+ status_ = std::move(status);
+ 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 (!ok()) Helper::Crash(status_);
+ }
+
+ void EnsureNotOk() {
+ if (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 class to allow implicitly deleted constructors and assignment
+// operations in StatusOr.
+// TraitsBase will explicitly delete what it can't support and StatusOr will
+// inherit that behavior implicitly.
+template <bool Copy, bool Move>
+struct TraitsBase {
+ TraitsBase() = default;
+ TraitsBase(const TraitsBase&) = default;
+ TraitsBase(TraitsBase&&) = default;
+ TraitsBase& operator=(const TraitsBase&) = default;
+ TraitsBase& operator=(TraitsBase&&) = default;
+};
+
+template <>
+struct TraitsBase<false, true> {
+ TraitsBase() = default;
+ TraitsBase(const TraitsBase&) = delete;
+ TraitsBase(TraitsBase&&) = default;
+ TraitsBase& operator=(const TraitsBase&) = delete;
+ TraitsBase& operator=(TraitsBase&&) = default;
+};
+
+template <>
+struct TraitsBase<false, false> {
+ TraitsBase() = default;
+ TraitsBase(const TraitsBase&) = delete;
+ TraitsBase(TraitsBase&&) = delete;
+ TraitsBase& operator=(const TraitsBase&) = delete;
+ TraitsBase& operator=(TraitsBase&&) = delete;
+};
+
+} // namespace internal_statusor
+} // namespace port
+} // namespace stream_executor
+
+#endif // TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_INTERNALS_H_
diff --git a/tensorflow/stream_executor/lib/statusor_test.cc b/tensorflow/stream_executor/lib/statusor_test.cc
new file mode 100644
index 0000000000..56584e1892
--- /dev/null
+++ b/tensorflow/stream_executor/lib/statusor_test.cc
@@ -0,0 +1,676 @@
+/* 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.
+==============================================================================*/
+
+// Unit tests for StatusOr
+
+#include "tensorflow/stream_executor/lib/statusor.h"
+
+#include <memory>
+#include <type_traits>
+
+#include "tensorflow/core/platform/test.h"
+#include "tensorflow/core/lib/core/errors.h"
+#include "tensorflow/core/platform/macros.h"
+#include "tensorflow/core/platform/test_benchmark.h"
+
+namespace stream_executor {
+namespace port {
+namespace {
+
+class Base1 {
+ public:
+ virtual ~Base1() {}
+ int pad_;
+};
+
+class Base2 {
+ public:
+ virtual ~Base2() {}
+ int yetotherpad_;
+};
+
+class Derived : public Base1, public Base2 {
+ public:
+ ~Derived() override {}
+ 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&);
+};
+
+class NoDefaultConstructor {
+ public:
+ explicit NoDefaultConstructor(int foo);
+};
+
+static_assert(!std::is_default_constructible<NoDefaultConstructor>(),
+ "Should not be default-constructible.");
+
+StatusOr<std::unique_ptr<int>> ReturnUniquePtr() {
+ // Uses implicit constructor from T&&
+ return std::unique_ptr<int>(new int(0));
+}
+
+TEST(StatusOr, ElementType) {
+ static_assert(std::is_same<StatusOr<int>::element_type, int>(), "");
+ static_assert(std::is_same<StatusOr<char>::element_type, char>(), "");
+}
+
+TEST(StatusOr, NullPointerStatusOr) {
+ // As a very special case, null-plain-pointer StatusOr used to be an
+ // error. Test that it no longer is.
+ StatusOr<int*> null_status(nullptr);
+ EXPECT_TRUE(null_status.ok());
+ EXPECT_EQ(null_status.ValueOrDie(), nullptr);
+}
+
+TEST(StatusOr, TestNoDefaultConstructorInitialization) {
+ // Explicitly initialize it with an error code.
+ StatusOr<NoDefaultConstructor> statusor(tensorflow::errors::Cancelled(""));
+ EXPECT_FALSE(statusor.ok());
+ EXPECT_EQ(statusor.status().code(), tensorflow::error::CANCELLED);
+
+ // Default construction of StatusOr initializes it with an UNKNOWN error code.
+ StatusOr<NoDefaultConstructor> statusor2;
+ EXPECT_FALSE(statusor2.ok());
+ EXPECT_EQ(statusor2.status().code(), tensorflow::error::UNKNOWN);
+}
+
+TEST(StatusOr, TestMoveOnlyInitialization) {
+ StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr());
+ ASSERT_TRUE(thing.ok());
+ EXPECT_EQ(0, *thing.ValueOrDie());
+ int* previous = thing.ValueOrDie().get();
+
+ thing = ReturnUniquePtr();
+ EXPECT_TRUE(thing.ok());
+ EXPECT_EQ(0, *thing.ValueOrDie());
+ EXPECT_NE(previous, thing.ValueOrDie().get());
+}
+
+TEST(StatusOr, TestMoveOnlyStatusCtr) {
+ StatusOr<std::unique_ptr<int>> thing(tensorflow::errors::Cancelled(""));
+ ASSERT_FALSE(thing.ok());
+}
+
+TEST(StatusOr, TestMoveOnlyValueExtraction) {
+ StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr());
+ ASSERT_TRUE(thing.ok());
+ std::unique_ptr<int> ptr = thing.ConsumeValueOrDie();
+ EXPECT_EQ(0, *ptr);
+
+ thing = std::move(ptr);
+ ptr = std::move(thing.ValueOrDie());
+ EXPECT_EQ(0, *ptr);
+}
+
+TEST(StatusOr, TestMoveOnlyConversion) {
+ StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr());
+ EXPECT_TRUE(const_thing.ok());
+ EXPECT_EQ(0, *const_thing.ValueOrDie());
+
+ // Test rvalue converting assignment
+ const int* const_previous = const_thing.ValueOrDie().get();
+ const_thing = ReturnUniquePtr();
+ EXPECT_TRUE(const_thing.ok());
+ EXPECT_EQ(0, *const_thing.ValueOrDie());
+ EXPECT_NE(const_previous, const_thing.ValueOrDie().get());
+}
+
+TEST(StatusOr, TestMoveOnlyVector) {
+ // Sanity check that StatusOr<MoveOnly> works in vector.
+ std::vector<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].ValueOrDie());
+ EXPECT_EQ(tensorflow::error::UNKNOWN, another_vec[1].status().code());
+}
+
+TEST(StatusOr, TestMoveWithValuesAndErrors) {
+ StatusOr<string> status_or(string(1000, '0'));
+ StatusOr<string> value1(string(1000, '1'));
+ StatusOr<string> value2(string(1000, '2'));
+ StatusOr<string> error1(Status(tensorflow::error::UNKNOWN, "error1"));
+ StatusOr<string> error2(Status(tensorflow::error::UNKNOWN, "error2"));
+
+ ASSERT_TRUE(status_or.ok());
+ EXPECT_EQ(string(1000, '0'), status_or.ValueOrDie());
+
+ // Overwrite the value in status_or with another value.
+ status_or = std::move(value1);
+ ASSERT_TRUE(status_or.ok());
+ EXPECT_EQ(string(1000, '1'), status_or.ValueOrDie());
+
+ // Overwrite the value in status_or with an error.
+ status_or = std::move(error1);
+ ASSERT_FALSE(status_or.ok());
+ EXPECT_EQ("error1", status_or.status().error_message());
+
+ // Overwrite the error in status_or with another error.
+ status_or = std::move(error2);
+ ASSERT_FALSE(status_or.ok());
+ EXPECT_EQ("error2", status_or.status().error_message());
+
+ // Overwrite the error with a value.
+ status_or = std::move(value2);
+ ASSERT_TRUE(status_or.ok());
+ EXPECT_EQ(string(1000, '2'), status_or.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyWithValuesAndErrors) {
+ StatusOr<string> status_or(string(1000, '0'));
+ StatusOr<string> value1(string(1000, '1'));
+ StatusOr<string> value2(string(1000, '2'));
+ StatusOr<string> error1(Status(tensorflow::error::UNKNOWN, "error1"));
+ StatusOr<string> error2(Status(tensorflow::error::UNKNOWN, "error2"));
+
+ ASSERT_TRUE(status_or.ok());
+ EXPECT_EQ(string(1000, '0'), status_or.ValueOrDie());
+
+ // Overwrite the value in status_or with another value.
+ status_or = value1;
+ ASSERT_TRUE(status_or.ok());
+ EXPECT_EQ(string(1000, '1'), status_or.ValueOrDie());
+
+ // Overwrite the value in status_or with an error.
+ status_or = error1;
+ ASSERT_FALSE(status_or.ok());
+ EXPECT_EQ("error1", status_or.status().error_message());
+
+ // Overwrite the error in status_or with another error.
+ status_or = error2;
+ ASSERT_FALSE(status_or.ok());
+ EXPECT_EQ("error2", status_or.status().error_message());
+
+ // Overwrite the error with a value.
+ status_or = value2;
+ ASSERT_TRUE(status_or.ok());
+ EXPECT_EQ(string(1000, '2'), status_or.ValueOrDie());
+
+ // Verify original values unchanged.
+ EXPECT_EQ(string(1000, '1'), value1.ValueOrDie());
+ EXPECT_EQ("error1", error1.status().error_message());
+ EXPECT_EQ("error2", error2.status().error_message());
+ EXPECT_EQ(string(1000, '2'), value2.ValueOrDie());
+}
+
+TEST(StatusOr, TestDefaultCtor) {
+ StatusOr<int> thing;
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(thing.status().code(), tensorflow::error::UNKNOWN);
+}
+
+TEST(StatusOrDeathTest, TestDefaultCtorValue) {
+ StatusOr<int> thing;
+ EXPECT_DEATH(thing.ValueOrDie(), "");
+
+ const StatusOr<int> thing2;
+ EXPECT_DEATH(thing.ValueOrDie(), "");
+}
+
+TEST(StatusOr, TestStatusCtor) {
+ StatusOr<int> thing(Status(tensorflow::error::CANCELLED, ""));
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(thing.status().code(), tensorflow::error::CANCELLED);
+}
+
+TEST(StatusOr, TestValueCtor) {
+ const int kI = 4;
+ const StatusOr<int> thing(kI);
+ EXPECT_TRUE(thing.ok());
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusOk) {
+ const int kI = 4;
+ const StatusOr<int> original(kI);
+ const StatusOr<int> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+ EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusNotOk) {
+ StatusOr<int> original(Status(tensorflow::error::CANCELLED, ""));
+ StatusOr<int> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+}
+
+TEST(StatusOr, TestCopyCtorNonAssignable) {
+ const int kI = 4;
+ CopyNoAssign value(kI);
+ StatusOr<CopyNoAssign> original(value);
+ StatusOr<CopyNoAssign> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+ EXPECT_EQ(original.ValueOrDie().foo_, copy.ValueOrDie().foo_);
+}
+
+TEST(StatusOr, TestCopyCtorStatusOKConverting) {
+ const int kI = 4;
+ StatusOr<int> original(kI);
+ StatusOr<double> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+ EXPECT_DOUBLE_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusNotOkConverting) {
+ StatusOr<int> original(Status(tensorflow::error::CANCELLED, ""));
+ StatusOr<double> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+}
+
+TEST(StatusOr, TestAssignmentStatusOk) {
+ const int kI = 4;
+ StatusOr<int> source(kI);
+ StatusOr<int> target;
+ target = source;
+ EXPECT_EQ(target.status(), source.status());
+ EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestAssignmentStatusNotOk) {
+ StatusOr<int> source(Status(tensorflow::error::CANCELLED, ""));
+ StatusOr<int> target;
+ target = source;
+ EXPECT_EQ(target.status(), source.status());
+}
+
+TEST(StatusOr, TestStatus) {
+ StatusOr<int> good(4);
+ EXPECT_TRUE(good.ok());
+ StatusOr<int> bad(Status(tensorflow::error::CANCELLED, ""));
+ EXPECT_FALSE(bad.ok());
+ EXPECT_EQ(bad.status(), Status(tensorflow::error::CANCELLED, ""));
+}
+
+TEST(StatusOr, TestValue) {
+ const int kI = 4;
+ StatusOr<int> thing(kI);
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestValueConst) {
+ const int kI = 4;
+ const StatusOr<int> thing(kI);
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOrDeathTest, TestValueNotOk) {
+ StatusOr<int> thing(Status(tensorflow::error::CANCELLED, "cancelled"));
+ EXPECT_DEATH(thing.ValueOrDie(), "cancelled");
+}
+
+TEST(StatusOrDeathTest, TestValueNotOkConst) {
+ const StatusOr<int> thing(Status(tensorflow::error::UNKNOWN, ""));
+ EXPECT_DEATH(thing.ValueOrDie(), "");
+}
+
+TEST(StatusOr, TestPointerDefaultCtor) {
+ StatusOr<int*> thing;
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(thing.status().code(), tensorflow::error::UNKNOWN);
+}
+
+TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) {
+ StatusOr<int*> thing;
+ EXPECT_DEATH(thing.ValueOrDie(), "");
+}
+
+TEST(StatusOr, TestPointerStatusCtor) {
+ StatusOr<int*> thing(Status(tensorflow::error::CANCELLED, ""));
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(thing.status(), Status(tensorflow::error::CANCELLED, ""));
+}
+
+TEST(StatusOr, TestPointerValueCtor) {
+ const int kI = 4;
+ StatusOr<const int*> thing(&kI);
+ EXPECT_TRUE(thing.ok());
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusOk) {
+ const int kI = 0;
+ StatusOr<const int*> original(&kI);
+ StatusOr<const int*> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+ EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusNotOk) {
+ StatusOr<int*> original(Status(tensorflow::error::CANCELLED, ""));
+ StatusOr<int*> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) {
+ Derived derived;
+ StatusOr<Derived*> original(&derived);
+ StatusOr<Base2*> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+ EXPECT_EQ(static_cast<const Base2*>(original.ValueOrDie()),
+ copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) {
+ StatusOr<Derived*> original(Status(tensorflow::error::CANCELLED, ""));
+ StatusOr<Base2*> copy(original);
+ EXPECT_EQ(copy.status(), original.status());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusOk) {
+ const int kI = 0;
+ StatusOr<const int*> source(&kI);
+ StatusOr<const int*> target;
+ target = source;
+ EXPECT_EQ(target.status(), source.status());
+ EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusNotOk) {
+ StatusOr<int*> source(Status(tensorflow::error::CANCELLED, ""));
+ StatusOr<int*> target;
+ target = source;
+ EXPECT_EQ(target.status(), source.status());
+}
+
+TEST(StatusOr, TestPointerStatus) {
+ const int kI = 0;
+ StatusOr<const int*> good(&kI);
+ EXPECT_TRUE(good.ok());
+ StatusOr<const int*> bad(Status(tensorflow::error::CANCELLED, ""));
+ EXPECT_EQ(bad.status(), Status(tensorflow::error::CANCELLED, ""));
+}
+
+TEST(StatusOr, TestPointerValue) {
+ const int kI = 0;
+ StatusOr<const int*> thing(&kI);
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerValueConst) {
+ const int kI = 0;
+ const StatusOr<const int*> thing(&kI);
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+// NOTE(tucker): StatusOr does not support this kind
+// of resize op.
+// TEST(StatusOr, StatusOrVectorOfUniquePointerCanResize) {
+// using EvilType = std::vector<std::unique_ptr<int>>;
+// static_assert(std::is_copy_constructible<EvilType>::value, "");
+// std::vector<StatusOr<EvilType>> v(5);
+// v.reserve(v.capacity() + 10);
+// }
+
+TEST(StatusOrDeathTest, TestPointerValueNotOk) {
+ StatusOr<int*> thing(Status(tensorflow::error::CANCELLED, "cancelled"));
+ EXPECT_DEATH(thing.ValueOrDie(), "cancelled");
+}
+
+TEST(StatusOrDeathTest, TestPointerValueNotOkConst) {
+ const StatusOr<int*> thing(Status(tensorflow::error::CANCELLED, "cancelled"));
+ EXPECT_DEATH(thing.ValueOrDie(), "cancelled");
+}
+
+static StatusOr<int> MakeStatus() { return 100; }
+// A factory to help us benchmark the various factory styles. All of
+// the factory methods are marked as non-inlineable so as to more
+// accurately simulate calling a factory for which you do not have
+// visibility of implementation. Similarly, the value_ variable is
+// marked volatile to prevent the compiler from getting too clever
+// about detecting that the same value is used in all loop iterations.
+template <typename T>
+class BenchmarkFactory {
+ public:
+ // Construct a new factory. Allocate an object which will always
+ // be the result of the factory methods.
+ BenchmarkFactory() : value_(new T) {}
+
+ // Destroy this factory, including the result value.
+ ~BenchmarkFactory() { delete value_; }
+
+ // A trivial factory that just returns the value. There is no status
+ // object that could be returned to encapsulate an error
+ T* TrivialFactory() TF_ATTRIBUTE_NOINLINE { return value_; }
+
+ // A more sophisticated factory, which returns a status to indicate
+ // the result of the operation. The factory result is populated into
+ // the user provided pointer result.
+ Status ArgumentFactory(T** result) TF_ATTRIBUTE_NOINLINE {
+ *result = value_;
+ return Status::OK();
+ }
+
+ Status ArgumentFactoryFail(T** result) TF_ATTRIBUTE_NOINLINE {
+ *result = nullptr;
+ return Status(tensorflow::error::CANCELLED, "");
+ }
+
+ Status ArgumentFactoryFailShortMsg(T** result) TF_ATTRIBUTE_NOINLINE {
+ *result = nullptr;
+ return Status(::tensorflow::error::INTERNAL, "");
+ }
+
+ Status ArgumentFactoryFailLongMsg(T** result) TF_ATTRIBUTE_NOINLINE {
+ *result = nullptr;
+ return Status(::tensorflow::error::INTERNAL,
+ "a big string of message junk that will never be read");
+ }
+
+ // A factory that returns a StatusOr<T*>. If the factory operation
+ // is OK, then the StatusOr<T*> will hold a T*. Otherwise, it will
+ // hold a status explaining the error.
+ StatusOr<T*> StatusOrFactory() TF_ATTRIBUTE_NOINLINE {
+ return static_cast<T*>(value_);
+ }
+
+ StatusOr<T*> StatusOrFactoryFail() TF_ATTRIBUTE_NOINLINE {
+ return Status(tensorflow::error::CANCELLED, "");
+ }
+
+ StatusOr<T*> StatusOrFactoryFailShortMsg() TF_ATTRIBUTE_NOINLINE {
+ return Status(::tensorflow::error::INTERNAL, "");
+ }
+
+ StatusOr<T*> StatusOrFactoryFailLongMsg() TF_ATTRIBUTE_NOINLINE {
+ return Status(::tensorflow::error::INTERNAL,
+ "a big string of message junk that will never be read");
+ }
+
+ private:
+ T* volatile value_;
+ TF_DISALLOW_COPY_AND_ASSIGN(BenchmarkFactory);
+};
+
+// A simple type we use with the factory.
+class BenchmarkType {
+ public:
+ BenchmarkType() {}
+ virtual ~BenchmarkType() {}
+ virtual void DoWork() TF_ATTRIBUTE_NOINLINE {}
+
+ private:
+ TF_DISALLOW_COPY_AND_ASSIGN(BenchmarkType);
+};
+
+// Calibrate the amount of time spent just calling DoWork, since each of our
+// tests will do this, we can subtract this out of benchmark results.
+void BM_CalibrateWorkLoop(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ BenchmarkType* result = factory.TrivialFactory();
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ if (result != nullptr) {
+ result->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_CalibrateWorkLoop);
+
+// Measure the time taken to call into the factory, return the value,
+// determine that it is OK, and invoke a trivial function.
+void BM_TrivialFactory(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ BenchmarkType* result = factory.TrivialFactory();
+ if (result != nullptr) {
+ result->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_TrivialFactory);
+
+// Measure the time taken to call into the factory, providing an
+// out-param for the result, evaluating the status result and the
+// result pointer, and invoking the trivial function.
+void BM_ArgumentFactory(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ BenchmarkType* result = nullptr;
+ Status status = factory.ArgumentFactory(&result);
+ if (status.ok() && result != nullptr) {
+ result->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_ArgumentFactory);
+
+// Measure the time to use the StatusOr<T*> factory, evaluate the result,
+// and invoke the trivial function.
+void BM_StatusOrFactory(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ StatusOr<BenchmarkType*> result = factory.StatusOrFactory();
+ if (result.ok()) {
+ result.ValueOrDie()->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_StatusOrFactory);
+
+// Measure the time taken to call into the factory, providing an
+// out-param for the result, evaluating the status result and the
+// result pointer, and invoking the trivial function.
+void BM_ArgumentFactoryFail(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ BenchmarkType* result = nullptr;
+ Status status = factory.ArgumentFactoryFail(&result);
+ if (status.ok() && result != nullptr) {
+ result->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_ArgumentFactoryFail);
+
+// Measure the time to use the StatusOr<T*> factory, evaluate the result,
+// and invoke the trivial function.
+void BM_StatusOrFactoryFail(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFail();
+ if (result.ok()) {
+ result.ValueOrDie()->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_StatusOrFactoryFail);
+
+// Measure the time taken to call into the factory, providing an
+// out-param for the result, evaluating the status result and the
+// result pointer, and invoking the trivial function.
+void BM_ArgumentFactoryFailShortMsg(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ BenchmarkType* result = nullptr;
+ Status status = factory.ArgumentFactoryFailShortMsg(&result);
+ if (status.ok() && result != nullptr) {
+ result->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_ArgumentFactoryFailShortMsg);
+
+// Measure the time to use the StatusOr<T*> factory, evaluate the result,
+// and invoke the trivial function.
+void BM_StatusOrFactoryFailShortMsg(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFailShortMsg();
+ if (result.ok()) {
+ result.ValueOrDie()->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_StatusOrFactoryFailShortMsg);
+
+// Measure the time taken to call into the factory, providing an
+// out-param for the result, evaluating the status result and the
+// result pointer, and invoking the trivial function.
+void BM_ArgumentFactoryFailLongMsg(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ BenchmarkType* result = nullptr;
+ Status status = factory.ArgumentFactoryFailLongMsg(&result);
+ if (status.ok() && result != nullptr) {
+ result->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_ArgumentFactoryFailLongMsg);
+
+// Measure the time to use the StatusOr<T*> factory, evaluate the result,
+// and invoke the trivial function.
+void BM_StatusOrFactoryFailLongMsg(int iters) {
+ tensorflow::testing::StopTiming();
+ BenchmarkFactory<BenchmarkType> factory;
+ tensorflow::testing::StartTiming();
+ for (int i = 0; i != iters; ++i) {
+ StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFailLongMsg();
+ if (result.ok()) {
+ result.ValueOrDie()->DoWork();
+ }
+ }
+}
+BENCHMARK(BM_StatusOrFactoryFailLongMsg);
+
+} // namespace
+} // namespace port
+} // namespace stream_executor