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_CORE_PLATFORM_STATUSOR_H_
69 #define TENSORFLOW_CORE_PLATFORM_STATUSOR_H_
70
71 #include "tensorflow/core/platform/macros.h"
72 #include "tensorflow/core/platform/status.h"
73 #include "tensorflow/core/platform/statusor_internals.h"
74
75 namespace tensorflow {
76
77 #if defined(__clang__)
78 // Only clang supports warn_unused_result as a type annotation.
79 template <typename T>
80 class TF_MUST_USE_RESULT StatusOr;
81 #endif
82
83 template <typename T>
84 class StatusOr : private internal_statusor::StatusOrData<T>,
85 private internal_statusor::TraitsBase<
86 std::is_copy_constructible<T>::value,
87 std::is_move_constructible<T>::value> {
88 template <typename U>
89 friend class StatusOr;
90
91 typedef internal_statusor::StatusOrData<T> Base;
92
93 public:
94 typedef T element_type; // DEPRECATED: use `value_type`.
95 typedef T value_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 the inner value `T` in-place using the provided args, using the
130 // `T(args...)` constructor.
131 template <typename... Args>
132 explicit StatusOr(absl::in_place_t, Args&&... args);
133
134 // Constructs a new StatusOr with the given value. After calling this
135 // constructor, calls to ValueOrDie() will succeed, and calls to status() will
136 // return OK.
137 //
138 // NOTE: Not explicit - we want to use StatusOr<T> as a return type
139 // so it is convenient and sensible to be able to do 'return T()'
140 // when the return type is StatusOr<T>.
141 //
142 // REQUIRES: T is copy constructible.
143 StatusOr(const T& value);
144
145 // Constructs a new StatusOr with the given non-ok status. After calling
146 // this constructor, calls to ValueOrDie() will CHECK-fail.
147 //
148 // NOTE: Not explicit - we want to use StatusOr<T> as a return
149 // value, so it is convenient and sensible to be able to do 'return
150 // Status()' when the return type is StatusOr<T>.
151 //
152 // REQUIRES: !status.ok(). This requirement is DCHECKed.
153 // In optimized builds, passing Status::OK() here will have the effect
154 // of passing tensorflow::error::INTERNAL as a fallback.
155 StatusOr(const Status& status);
156 StatusOr& operator=(const Status& status);
157
158 // TODO(b/62186997): Add operator=(T) overloads.
159
160 // Similar to the `const T&` overload.
161 //
162 // REQUIRES: T is move constructible.
163 StatusOr(T&& value);
164
165 // RValue versions of the operations declared above.
166 StatusOr(Status&& status);
167 StatusOr& operator=(Status&& status);
168
169 // Returns this->status().ok()
ok()170 bool ok() const { return this->status_.ok(); }
171
172 // Returns a reference to our status. If this contains a T, then
173 // returns Status::OK().
174 const Status& status() const&;
175 Status status() &&;
176
177 // StatusOr<T>::value()
178 //
179 // absl::StatusOr compatible versions of ValueOrDie.
180 const T& value() const&;
181 T& value() &;
182 const T&& value() const&&;
183 T&& value() &&;
184
185 // Returns a reference to our current value, or CHECK-fails if !this->ok().
186 //
187 // DEPRECATED: Prefer accessing the value using `operator*` or `operator->`
188 // after testing that the StatusOr is OK. If program termination is desired in
189 // the case of an error status, consider `CHECK_OK(status_or);`.
190 // Note: for value types that are cheap to copy, prefer simple code:
191 //
192 // T value = statusor.ValueOrDie();
193 //
194 // Otherwise, if the value type is expensive to copy, but can be left
195 // in the StatusOr, simply assign to a reference:
196 //
197 // T& value = statusor.ValueOrDie(); // or `const T&`
198 //
199 // Otherwise, if the value type supports an efficient move, it can be
200 // used as follows:
201 //
202 // T value = std::move(statusor).ValueOrDie();
203 //
204 // The std::move on statusor instead of on the whole expression enables
205 // warnings about possible uses of the statusor object after the move.
206 // C++ style guide waiver for ref-qualified overloads granted in cl/143176389
207 // See go/ref-qualifiers for more details on such overloads.
208 const T& ValueOrDie() const&;
209 T& ValueOrDie() &;
210 const T&& ValueOrDie() const&&;
211 T&& ValueOrDie() &&;
212
213 // Returns a reference to the current value.
214 //
215 // REQUIRES: this->ok() == true, otherwise the behavior is undefined.
216 //
217 // Use this->ok() or `operator bool()` to verify that there is a current
218 // value. Alternatively, see ValueOrDie() for a similar API that guarantees
219 // CHECK-failing if there is no current value.
220 const T& operator*() const&;
221 T& operator*() &;
222 const T&& operator*() const&&;
223 T&& operator*() &&;
224
225 // Returns a pointer to the current value.
226 //
227 // REQUIRES: this->ok() == true, otherwise the behavior is undefined.
228 //
229 // Use this->ok() or `operator bool()` to verify that there is a current
230 // value.
231 const T* operator->() const;
232 T* operator->();
233
234 // Ignores any errors. This method does nothing except potentially suppress
235 // complaints from any tools that are checking that errors are not dropped on
236 // the floor.
237 void IgnoreError() const;
238 };
239
240 ////////////////////////////////////////////////////////////////////////////////
241 // Implementation details for StatusOr<T>
242
243 template <typename T>
StatusOr()244 StatusOr<T>::StatusOr() : Base(Status(tensorflow::error::UNKNOWN, "")) {}
245
246 template <typename T>
StatusOr(const T & value)247 StatusOr<T>::StatusOr(const T& value) : Base(value) {}
248
249 template <typename T>
StatusOr(const Status & status)250 StatusOr<T>::StatusOr(const Status& status) : Base(status) {}
251
252 template <typename T>
253 StatusOr<T>& StatusOr<T>::operator=(const Status& status) {
254 this->Assign(status);
255 return *this;
256 }
257
258 template <typename T>
StatusOr(T && value)259 StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {}
260
261 template <typename T>
262 template <typename... Args>
StatusOr(absl::in_place_t,Args &&...args)263 StatusOr<T>::StatusOr(absl::in_place_t, Args&&... args)
264 : Base(absl::in_place, std::forward<Args>(args)...) {}
265
266 template <typename T>
StatusOr(Status && status)267 StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {}
268
269 template <typename T>
270 StatusOr<T>& StatusOr<T>::operator=(Status&& status) {
271 this->Assign(std::move(status));
272 return *this;
273 }
274
275 template <typename T>
276 template <typename U,
277 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(const StatusOr<U> & other)278 inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
279 : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
280
281 template <typename T>
282 template <typename U,
283 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
284 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
285 if (other.ok())
286 this->Assign(other.ValueOrDie());
287 else
288 this->Assign(other.status());
289 return *this;
290 }
291
292 template <typename T>
293 template <typename U,
294 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(StatusOr<U> && other)295 inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
296 : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
297
298 template <typename T>
299 template <typename U,
300 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
301 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
302 if (other.ok()) {
303 this->Assign(std::move(other).ValueOrDie());
304 } else {
305 this->Assign(std::move(other).status());
306 }
307 return *this;
308 }
309
310 template <typename T>
status()311 const Status& StatusOr<T>::status() const& {
312 return this->status_;
313 }
314 template <typename T>
status()315 Status StatusOr<T>::status() && {
316 // Note that we copy instead of moving the status here so that
317 // ~StatusOrData() can call ok() without invoking UB.
318 return ok() ? OkStatus() : this->status_;
319 }
320
321 template <typename T>
value()322 const T& StatusOr<T>::value() const& {
323 this->EnsureOk();
324 return this->data_;
325 }
326
327 template <typename T>
value()328 T& StatusOr<T>::value() & {
329 this->EnsureOk();
330 return this->data_;
331 }
332
333 template <typename T>
value()334 const T&& StatusOr<T>::value() const&& {
335 this->EnsureOk();
336 return std::move(this->data_);
337 }
338
339 template <typename T>
value()340 T&& StatusOr<T>::value() && {
341 this->EnsureOk();
342 return std::move(this->data_);
343 }
344
345 template <typename T>
ValueOrDie()346 const T& StatusOr<T>::ValueOrDie() const& {
347 this->EnsureOk();
348 return this->data_;
349 }
350
351 template <typename T>
ValueOrDie()352 T& StatusOr<T>::ValueOrDie() & {
353 this->EnsureOk();
354 return this->data_;
355 }
356
357 template <typename T>
ValueOrDie()358 const T&& StatusOr<T>::ValueOrDie() const&& {
359 this->EnsureOk();
360 return std::move(this->data_);
361 }
362
363 template <typename T>
ValueOrDie()364 T&& StatusOr<T>::ValueOrDie() && {
365 this->EnsureOk();
366 return std::move(this->data_);
367 }
368
369 template <typename T>
370 const T* StatusOr<T>::operator->() const {
371 this->EnsureOk();
372 return &this->data_;
373 }
374
375 template <typename T>
376 T* StatusOr<T>::operator->() {
377 this->EnsureOk();
378 return &this->data_;
379 }
380
381 template <typename T>
382 const T& StatusOr<T>::operator*() const& {
383 this->EnsureOk();
384 return this->data_;
385 }
386
387 template <typename T>
388 T& StatusOr<T>::operator*() & {
389 this->EnsureOk();
390 return this->data_;
391 }
392
393 template <typename T>
394 const T&& StatusOr<T>::operator*() const&& {
395 this->EnsureOk();
396 return std::move(this->data_);
397 }
398
399 template <typename T>
400 T&& StatusOr<T>::operator*() && {
401 this->EnsureOk();
402 return std::move(this->data_);
403 }
404
405 template <typename T>
IgnoreError()406 void StatusOr<T>::IgnoreError() const {
407 // no-op
408 }
409
410 #define TF_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
411 TF_ASSERT_OK_AND_ASSIGN_IMPL( \
412 TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
413 rexpr);
414
415 #define TF_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \
416 auto statusor = (rexpr); \
417 ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \
418 lhs = std::move(statusor).ValueOrDie()
419
420 #define TF_STATUS_MACROS_CONCAT_NAME(x, y) TF_STATUS_MACROS_CONCAT_IMPL(x, y)
421 #define TF_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
422
423 #define TF_ASSIGN_OR_RETURN(lhs, rexpr) \
424 TF_ASSIGN_OR_RETURN_IMPL( \
425 TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr)
426
427 #define TF_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \
428 auto statusor = (rexpr); \
429 if (TF_PREDICT_FALSE(!statusor.ok())) { \
430 return statusor.status(); \
431 } \
432 lhs = std::move(statusor).ValueOrDie()
433
434 } // namespace tensorflow
435
436 #endif // TENSORFLOW_CORE_PLATFORM_STATUSOR_H_
437