xref: /aosp_15_r20/external/tensorflow/tensorflow/core/platform/statusor.h (revision b6fb3261f9314811a0f4371741dbb8839866f948)
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