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; // DEPRECATED: use `value_type`.
96 typedef T value_type;
97
98 // Constructs a new StatusOr with Status::UNKNOWN status. This is marked
99 // 'explicit' to try to catch cases like 'return {};', where people think
100 // StatusOr<std::vector<int>> will be initialized with an empty vector,
101 // instead of a Status::UNKNOWN status.
102 explicit StatusOr();
103
104 // StatusOr<T> will be copy constructible/assignable if T is copy
105 // constructible.
106 StatusOr(const StatusOr&) = default;
107 StatusOr& operator=(const StatusOr&) = default;
108
109 // StatusOr<T> will be move constructible/assignable if T is move
110 // constructible.
111 StatusOr(StatusOr&&) = default;
112 StatusOr& operator=(StatusOr&&) = default;
113
114 // Conversion copy/move constructor, T must be convertible from U.
115 template <typename U, typename std::enable_if<
116 std::is_convertible<U, T>::value>::type* = nullptr>
117 StatusOr(const StatusOr<U>& other);
118 template <typename U, typename std::enable_if<
119 std::is_convertible<U, T>::value>::type* = nullptr>
120 StatusOr(StatusOr<U>&& other);
121
122 // Conversion copy/move assignment operator, T must be convertible from U.
123 template <typename U, typename std::enable_if<
124 std::is_convertible<U, T>::value>::type* = nullptr>
125 StatusOr& operator=(const StatusOr<U>& other);
126 template <typename U, typename std::enable_if<
127 std::is_convertible<U, T>::value>::type* = nullptr>
128 StatusOr& operator=(StatusOr<U>&& other);
129
130 // Constructs a new StatusOr with the given value. After calling this
131 // constructor, calls to ValueOrDie() will succeed, and calls to status() will
132 // return OK.
133 //
134 // NOTE: Not explicit - we want to use StatusOr<T> as a return type
135 // so it is convenient and sensible to be able to do 'return T()'
136 // when the return type is StatusOr<T>.
137 //
138 // REQUIRES: T is copy constructible.
139 StatusOr(const T& value);
140
141 // Constructs a new StatusOr with the given non-ok status. After calling
142 // this constructor, calls to ValueOrDie() will CHECK-fail.
143 //
144 // NOTE: Not explicit - we want to use StatusOr<T> as a return
145 // value, so it is convenient and sensible to be able to do 'return
146 // Status()' when the return type is StatusOr<T>.
147 //
148 // REQUIRES: !status.ok(). This requirement is DCHECKed.
149 // In optimized builds, passing Status::OK() here will have the effect
150 // of passing tensorflow::error::INTERNAL as a fallback.
151 StatusOr(const Status& status);
152 StatusOr& operator=(const Status& status);
153
154 // TODO(b/62186997): Add operator=(T) overloads.
155
156 // Similar to the `const T&` overload.
157 //
158 // REQUIRES: T is move constructible.
159 StatusOr(T&& value);
160
161 // RValue versions of the operations declared above.
162 StatusOr(Status&& status);
163 StatusOr& operator=(Status&& status);
164
165 // Returns this->status().ok()
ok()166 bool ok() const { return this->status_.ok(); }
167
168 // Returns a reference to our status. If this contains a T, then
169 // returns Status::OK().
170 const Status& status() const &;
171 Status status() &&;
172
173 // Returns a reference to our current value, or CHECK-fails if !this->ok().
174 //
175 // Note: for value types that are cheap to copy, prefer simple code:
176 //
177 // T value = statusor.ValueOrDie();
178 //
179 // Otherwise, if the value type is expensive to copy, but can be left
180 // in the StatusOr, simply assign to a reference:
181 //
182 // T& value = statusor.ValueOrDie(); // or `const T&`
183 //
184 // Otherwise, if the value type supports an efficient move, it can be
185 // used as follows:
186 //
187 // T value = std::move(statusor).ValueOrDie();
188 //
189 // The std::move on statusor instead of on the whole expression enables
190 // warnings about possible uses of the statusor object after the move.
191 // C++ style guide waiver for ref-qualified overloads granted in cl/143176389
192 // See go/ref-qualifiers for more details on such overloads.
193 const T& ValueOrDie() const &;
194 T& ValueOrDie() &;
195 const T&& ValueOrDie() const &&;
196 T&& ValueOrDie() &&;
197
198 // Returns a reference to the current value.
199 //
200 // REQUIRES: this->ok() == true, otherwise the behavior is undefined.
201 //
202 // Use this->ok() or `operator bool()` to verify that there is a current
203 // value. Alternatively, see ValueOrDie() for a similar API that guarantees
204 // CHECK-failing if there is no current value.
205 const T& operator*() const&;
206 T& operator*() &;
207 const T&& operator*() const&&;
208 T&& operator*() &&;
209
210 // Returns a pointer to the current value.
211 //
212 // REQUIRES: this->ok() == true, otherwise the behavior is undefined.
213 //
214 // Use this->ok() or `operator bool()` to verify that there is a current
215 // value.
216 const T* operator->() const;
217 T* operator->();
218
ConsumeValueOrDie()219 T ConsumeValueOrDie() { return std::move(ValueOrDie()); }
220
221 // Ignores any errors. This method does nothing except potentially suppress
222 // complaints from any tools that are checking that errors are not dropped on
223 // the floor.
224 void IgnoreError() const;
225 };
226
227 ////////////////////////////////////////////////////////////////////////////////
228 // Implementation details for StatusOr<T>
229
230 template <typename T>
StatusOr()231 StatusOr<T>::StatusOr() : Base(Status(tensorflow::error::UNKNOWN, "")) {}
232
233 template <typename T>
StatusOr(const T & value)234 StatusOr<T>::StatusOr(const T& value) : Base(value) {}
235
236 template <typename T>
StatusOr(const Status & status)237 StatusOr<T>::StatusOr(const Status& status) : Base(status) {}
238
239 template <typename T>
240 StatusOr<T>& StatusOr<T>::operator=(const Status& status) {
241 this->Assign(status);
242 return *this;
243 }
244
245 template <typename T>
StatusOr(T && value)246 StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {}
247
248 template <typename T>
StatusOr(Status && status)249 StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {}
250
251 template <typename T>
252 StatusOr<T>& StatusOr<T>::operator=(Status&& status) {
253 this->Assign(std::move(status));
254 return *this;
255 }
256
257 template <typename T>
258 template <typename U,
259 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(const StatusOr<U> & other)260 inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
261 : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
262
263 template <typename T>
264 template <typename U,
265 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
266 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
267 if (other.ok())
268 this->Assign(other.ValueOrDie());
269 else
270 this->Assign(other.status());
271 return *this;
272 }
273
274 template <typename T>
275 template <typename U,
276 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(StatusOr<U> && other)277 inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
278 : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
279
280 template <typename T>
281 template <typename U,
282 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
283 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
284 if (other.ok()) {
285 this->Assign(std::move(other).ValueOrDie());
286 } else {
287 this->Assign(std::move(other).status());
288 }
289 return *this;
290 }
291
292 template <typename T>
status()293 const Status& StatusOr<T>::status() const & {
294 return this->status_;
295 }
296 template <typename T>
status()297 Status StatusOr<T>::status() && {
298 // Note that we copy instead of moving the status here so that
299 // ~StatusOrData() can call ok() without invoking UB.
300 return ok() ? Status::OK() : this->status_;
301 }
302
303 template <typename T>
ValueOrDie()304 const T& StatusOr<T>::ValueOrDie() const & {
305 this->EnsureOk();
306 return this->data_;
307 }
308
309 template <typename T>
ValueOrDie()310 T& StatusOr<T>::ValueOrDie() & {
311 this->EnsureOk();
312 return this->data_;
313 }
314
315 template <typename T>
ValueOrDie()316 const T&& StatusOr<T>::ValueOrDie() const && {
317 this->EnsureOk();
318 return std::move(this->data_);
319 }
320
321 template <typename T>
ValueOrDie()322 T&& StatusOr<T>::ValueOrDie() && {
323 this->EnsureOk();
324 return std::move(this->data_);
325 }
326
327 template <typename T>
328 const T* StatusOr<T>::operator->() const {
329 this->EnsureOk();
330 return &this->data_;
331 }
332
333 template <typename T>
334 T* StatusOr<T>::operator->() {
335 this->EnsureOk();
336 return &this->data_;
337 }
338
339 template <typename T>
340 const T& StatusOr<T>::operator*() const& {
341 this->EnsureOk();
342 return this->data_;
343 }
344
345 template <typename T>
346 T& StatusOr<T>::operator*() & {
347 this->EnsureOk();
348 return this->data_;
349 }
350
351 template <typename T>
352 const T&& StatusOr<T>::operator*() const&& {
353 this->EnsureOk();
354 return std::move(this->data_);
355 }
356
357 template <typename T>
358 T&& StatusOr<T>::operator*() && {
359 this->EnsureOk();
360 return std::move(this->data_);
361 }
362
363 template <typename T>
IgnoreError()364 void StatusOr<T>::IgnoreError() const {
365 // no-op
366 }
367
368 } // namespace port
369
370 #define TF_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
371 TF_ASSERT_OK_AND_ASSIGN_IMPL( \
372 TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
373 rexpr);
374
375 #define TF_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \
376 auto statusor = (rexpr); \
377 ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \
378 lhs = std::move(statusor.ValueOrDie())
379
380 #define TF_STATUS_MACROS_CONCAT_NAME(x, y) TF_STATUS_MACROS_CONCAT_IMPL(x, y)
381 #define TF_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
382
383 #define TF_ASSIGN_OR_RETURN(lhs, rexpr) \
384 TF_ASSIGN_OR_RETURN_IMPL( \
385 TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr)
386
387 #define TF_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \
388 auto statusor = (rexpr); \
389 if (TF_PREDICT_FALSE(!statusor.ok())) { \
390 return statusor.status(); \
391 } \
392 lhs = std::move(statusor.ValueOrDie())
393
394 } // namespace stream_executor
395
396 #endif // TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_H_
397