1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 // StatusOr<T> is the union of a Status object and a T object. StatusOr models
17 // the concept of an object that is either a value, or an error Status
18 // explaining why such a value is not present. To this end, StatusOr<T> does not
19 // allow its Status value to be Status::OK.
20 //
21 // The primary use-case for StatusOr<T> is as the return value of a
22 // function which may fail.
23 //
24 // Example client usage for a StatusOr<T>, where T is not a pointer:
25 //
26 // StatusOr<float> result = DoBigCalculationThatCouldFail();
27 // if (result.ok()) {
28 // float answer = result.ValueOrDie();
29 // printf("Big calculation yielded: %f", answer);
30 // } else {
31 // LOG(ERROR) << result.status();
32 // }
33 //
34 // Example client usage for a StatusOr<T*>:
35 //
36 // StatusOr<Foo*> result = FooFactory::MakeNewFoo(arg);
37 // if (result.ok()) {
38 // std::unique_ptr<Foo> foo(result.ValueOrDie());
39 // foo->DoSomethingCool();
40 // } else {
41 // LOG(ERROR) << result.status();
42 // }
43 //
44 // Example client usage for a StatusOr<std::unique_ptr<T>>:
45 //
46 // StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
47 // if (result.ok()) {
48 // std::unique_ptr<Foo> foo = std::move(result.ValueOrDie());
49 // foo->DoSomethingCool();
50 // } else {
51 // LOG(ERROR) << result.status();
52 // }
53 //
54 // Example factory implementation returning StatusOr<T*>:
55 //
56 // StatusOr<Foo*> FooFactory::MakeNewFoo(int arg) {
57 // if (arg <= 0) {
58 // return tensorflow::InvalidArgument("Arg must be positive");
59 // } else {
60 // return new Foo(arg);
61 // }
62 // }
63 //
64 // Note that the assignment operators require that destroying the currently
65 // stored value cannot invalidate the argument; in other words, the argument
66 // cannot be an alias for the current value, or anything owned by the current
67 // value.
68 #ifndef TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_H_
69 #define TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_H_
70
71 #include "tensorflow/core/platform/macros.h"
72 #include "tensorflow/stream_executor/lib/status.h"
73 #include "tensorflow/stream_executor/lib/statusor_internals.h"
74
75 namespace stream_executor {
76 namespace port {
77
78 #if defined(__clang__)
79 // Only clang supports warn_unused_result as a type annotation.
80 template <typename T>
81 class TF_MUST_USE_RESULT StatusOr;
82 #endif
83
84 template <typename T>
85 class StatusOr : private internal_statusor::StatusOrData<T>,
86 private internal_statusor::TraitsBase<
87 std::is_copy_constructible<T>::value,
88 std::is_move_constructible<T>::value> {
89 template <typename U>
90 friend class StatusOr;
91
92 typedef internal_statusor::StatusOrData<T> Base;
93
94 public:
95 typedef T element_type;
96
97 // Constructs a new StatusOr with Status::UNKNOWN status. This is marked
98 // 'explicit' to try to catch cases like 'return {};', where people think
99 // StatusOr<std::vector<int>> will be initialized with an empty vector,
100 // instead of a Status::UNKNOWN status.
101 explicit StatusOr();
102
103 // StatusOr<T> will be copy constructible/assignable if T is copy
104 // constructible.
105 StatusOr(const StatusOr&) = default;
106 StatusOr& operator=(const StatusOr&) = default;
107
108 // StatusOr<T> will be move constructible/assignable if T is move
109 // constructible.
110 StatusOr(StatusOr&&) = default;
111 StatusOr& operator=(StatusOr&&) = default;
112
113 // Conversion copy/move constructor, T must be convertible from U.
114 template <typename U, typename std::enable_if<
115 std::is_convertible<U, T>::value>::type* = nullptr>
116 StatusOr(const StatusOr<U>& other);
117 template <typename U, typename std::enable_if<
118 std::is_convertible<U, T>::value>::type* = nullptr>
119 StatusOr(StatusOr<U>&& other);
120
121 // Conversion copy/move assignment operator, T must be convertible from U.
122 template <typename U, typename std::enable_if<
123 std::is_convertible<U, T>::value>::type* = nullptr>
124 StatusOr& operator=(const StatusOr<U>& other);
125 template <typename U, typename std::enable_if<
126 std::is_convertible<U, T>::value>::type* = nullptr>
127 StatusOr& operator=(StatusOr<U>&& other);
128
129 // Constructs a new StatusOr with the given value. After calling this
130 // constructor, calls to ValueOrDie() will succeed, and calls to status() will
131 // return OK.
132 //
133 // NOTE: Not explicit - we want to use StatusOr<T> as a return type
134 // so it is convenient and sensible to be able to do 'return T()'
135 // when the return type is StatusOr<T>.
136 //
137 // REQUIRES: T is copy constructible.
138 StatusOr(const T& value);
139
140 // Constructs a new StatusOr with the given non-ok status. After calling
141 // this constructor, calls to ValueOrDie() will CHECK-fail.
142 //
143 // NOTE: Not explicit - we want to use StatusOr<T> as a return
144 // value, so it is convenient and sensible to be able to do 'return
145 // Status()' when the return type is StatusOr<T>.
146 //
147 // REQUIRES: !status.ok(). This requirement is DCHECKed.
148 // In optimized builds, passing Status::OK() here will have the effect
149 // of passing tensorflow::error::INTERNAL as a fallback.
150 StatusOr(const Status& status);
151 StatusOr& operator=(const Status& status);
152
153 // TODO(b/62186997): Add operator=(T) overloads.
154
155 // Similar to the `const T&` overload.
156 //
157 // REQUIRES: T is move constructible.
158 StatusOr(T&& value);
159
160 // RValue versions of the operations declared above.
161 StatusOr(Status&& status);
162 StatusOr& operator=(Status&& status);
163
164 // Returns this->status().ok()
ok()165 bool ok() const { return this->status_.ok(); }
166
167 // Returns a reference to our status. If this contains a T, then
168 // returns Status::OK().
169 const Status& status() const &;
170 Status status() &&;
171
172 // Returns a reference to our current value, or CHECK-fails if !this->ok().
173 //
174 // Note: for value types that are cheap to copy, prefer simple code:
175 //
176 // T value = statusor.ValueOrDie();
177 //
178 // Otherwise, if the value type is expensive to copy, but can be left
179 // in the StatusOr, simply assign to a reference:
180 //
181 // T& value = statusor.ValueOrDie(); // or `const T&`
182 //
183 // Otherwise, if the value type supports an efficient move, it can be
184 // used as follows:
185 //
186 // T value = std::move(statusor).ValueOrDie();
187 //
188 // The std::move on statusor instead of on the whole expression enables
189 // warnings about possible uses of the statusor object after the move.
190 // C++ style guide waiver for ref-qualified overloads granted in cl/143176389
191 // See go/ref-qualifiers for more details on such overloads.
192 const T& ValueOrDie() const &;
193 T& ValueOrDie() &;
194 const T&& ValueOrDie() const &&;
195 T&& ValueOrDie() &&;
196
ConsumeValueOrDie()197 T ConsumeValueOrDie() { return std::move(ValueOrDie()); }
198
199 // Ignores any errors. This method does nothing except potentially suppress
200 // complaints from any tools that are checking that errors are not dropped on
201 // the floor.
202 void IgnoreError() const;
203 };
204
205 ////////////////////////////////////////////////////////////////////////////////
206 // Implementation details for StatusOr<T>
207
208 template <typename T>
StatusOr()209 StatusOr<T>::StatusOr() : Base(Status(tensorflow::error::UNKNOWN, "")) {}
210
211 template <typename T>
StatusOr(const T & value)212 StatusOr<T>::StatusOr(const T& value) : Base(value) {}
213
214 template <typename T>
StatusOr(const Status & status)215 StatusOr<T>::StatusOr(const Status& status) : Base(status) {}
216
217 template <typename T>
218 StatusOr<T>& StatusOr<T>::operator=(const Status& status) {
219 this->Assign(status);
220 return *this;
221 }
222
223 template <typename T>
StatusOr(T && value)224 StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {}
225
226 template <typename T>
StatusOr(Status && status)227 StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {}
228
229 template <typename T>
230 StatusOr<T>& StatusOr<T>::operator=(Status&& status) {
231 this->Assign(std::move(status));
232 return *this;
233 }
234
235 template <typename T>
236 template <typename U,
237 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(const StatusOr<U> & other)238 inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
239 : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
240
241 template <typename T>
242 template <typename U,
243 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
244 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
245 if (other.ok())
246 this->Assign(other.ValueOrDie());
247 else
248 this->Assign(other.status());
249 return *this;
250 }
251
252 template <typename T>
253 template <typename U,
254 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(StatusOr<U> && other)255 inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
256 : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
257
258 template <typename T>
259 template <typename U,
260 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
261 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
262 if (other.ok()) {
263 this->Assign(std::move(other).ValueOrDie());
264 } else {
265 this->Assign(std::move(other).status());
266 }
267 return *this;
268 }
269
270 template <typename T>
status()271 const Status& StatusOr<T>::status() const & {
272 return this->status_;
273 }
274 template <typename T>
status()275 Status StatusOr<T>::status() && {
276 // Note that we copy instead of moving the status here so that
277 // ~StatusOrData() can call ok() without invoking UB.
278 return ok() ? Status::OK() : this->status_;
279 }
280
281 template <typename T>
ValueOrDie()282 const T& StatusOr<T>::ValueOrDie() const & {
283 this->EnsureOk();
284 return this->data_;
285 }
286
287 template <typename T>
ValueOrDie()288 T& StatusOr<T>::ValueOrDie() & {
289 this->EnsureOk();
290 return this->data_;
291 }
292
293 template <typename T>
ValueOrDie()294 const T&& StatusOr<T>::ValueOrDie() const && {
295 this->EnsureOk();
296 return std::move(this->data_);
297 }
298
299 template <typename T>
ValueOrDie()300 T&& StatusOr<T>::ValueOrDie() && {
301 this->EnsureOk();
302 return std::move(this->data_);
303 }
304
305 template <typename T>
IgnoreError()306 void StatusOr<T>::IgnoreError() const {
307 // no-op
308 }
309
310 } // namespace port
311
312 #define TF_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
313 TF_ASSERT_OK_AND_ASSIGN_IMPL( \
314 TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
315 rexpr);
316
317 #define TF_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \
318 auto statusor = (rexpr); \
319 ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \
320 lhs = std::move(statusor.ValueOrDie())
321
322 #define TF_STATUS_MACROS_CONCAT_NAME(x, y) TF_STATUS_MACROS_CONCAT_IMPL(x, y)
323 #define TF_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
324
325 #define TF_ASSIGN_OR_RETURN(lhs, rexpr) \
326 TF_ASSIGN_OR_RETURN_IMPL( \
327 TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr)
328
329 #define TF_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \
330 auto statusor = (rexpr); \
331 if (TF_PREDICT_FALSE(!statusor.ok())) { \
332 return statusor.status(); \
333 } \
334 lhs = std::move(statusor.ValueOrDie())
335
336 } // namespace stream_executor
337
338 #endif // TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_H_
339