• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 
197   // Returns a reference to the current value.
198   //
199   // REQUIRES: this->ok() == true, otherwise the behavior is undefined.
200   //
201   // Use this->ok() or `operator bool()` to verify that there is a current
202   // value. Alternatively, see ValueOrDie() for a similar API that guarantees
203   // CHECK-failing if there is no current value.
204   const T& operator*() const&;
205   T& operator*() &;
206   const T&& operator*() const&&;
207   T&& operator*() &&;
208 
209   // Returns a pointer to the current value.
210   //
211   // REQUIRES: this->ok() == true, otherwise the behavior is undefined.
212   //
213   // Use this->ok() or `operator bool()` to verify that there is a current
214   // value.
215   const T* operator->() const;
216   T* operator->();
217 
ConsumeValueOrDie()218   T ConsumeValueOrDie() { return std::move(ValueOrDie()); }
219 
220   // Ignores any errors. This method does nothing except potentially suppress
221   // complaints from any tools that are checking that errors are not dropped on
222   // the floor.
223   void IgnoreError() const;
224 };
225 
226 ////////////////////////////////////////////////////////////////////////////////
227 // Implementation details for StatusOr<T>
228 
229 template <typename T>
StatusOr()230 StatusOr<T>::StatusOr() : Base(Status(tensorflow::error::UNKNOWN, "")) {}
231 
232 template <typename T>
StatusOr(const T & value)233 StatusOr<T>::StatusOr(const T& value) : Base(value) {}
234 
235 template <typename T>
StatusOr(const Status & status)236 StatusOr<T>::StatusOr(const Status& status) : Base(status) {}
237 
238 template <typename T>
239 StatusOr<T>& StatusOr<T>::operator=(const Status& status) {
240   this->Assign(status);
241   return *this;
242 }
243 
244 template <typename T>
StatusOr(T && value)245 StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {}
246 
247 template <typename T>
StatusOr(Status && status)248 StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {}
249 
250 template <typename T>
251 StatusOr<T>& StatusOr<T>::operator=(Status&& status) {
252   this->Assign(std::move(status));
253   return *this;
254 }
255 
256 template <typename T>
257 template <typename U,
258           typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(const StatusOr<U> & other)259 inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
260     : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
261 
262 template <typename T>
263 template <typename U,
264           typename std::enable_if<std::is_convertible<U, T>::value>::type*>
265 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
266   if (other.ok())
267     this->Assign(other.ValueOrDie());
268   else
269     this->Assign(other.status());
270   return *this;
271 }
272 
273 template <typename T>
274 template <typename U,
275           typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(StatusOr<U> && other)276 inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
277     : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
278 
279 template <typename T>
280 template <typename U,
281           typename std::enable_if<std::is_convertible<U, T>::value>::type*>
282 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
283   if (other.ok()) {
284     this->Assign(std::move(other).ValueOrDie());
285   } else {
286     this->Assign(std::move(other).status());
287   }
288   return *this;
289 }
290 
291 template <typename T>
status()292 const Status& StatusOr<T>::status() const & {
293   return this->status_;
294 }
295 template <typename T>
status()296 Status StatusOr<T>::status() && {
297   // Note that we copy instead of moving the status here so that
298   // ~StatusOrData() can call ok() without invoking UB.
299   return ok() ? Status::OK() : this->status_;
300 }
301 
302 template <typename T>
ValueOrDie()303 const T& StatusOr<T>::ValueOrDie() const & {
304   this->EnsureOk();
305   return this->data_;
306 }
307 
308 template <typename T>
ValueOrDie()309 T& StatusOr<T>::ValueOrDie() & {
310   this->EnsureOk();
311   return this->data_;
312 }
313 
314 template <typename T>
ValueOrDie()315 const T&& StatusOr<T>::ValueOrDie() const && {
316   this->EnsureOk();
317   return std::move(this->data_);
318 }
319 
320 template <typename T>
ValueOrDie()321 T&& StatusOr<T>::ValueOrDie() && {
322   this->EnsureOk();
323   return std::move(this->data_);
324 }
325 
326 template <typename T>
327 const T* StatusOr<T>::operator->() const {
328   this->EnsureOk();
329   return &this->data_;
330 }
331 
332 template <typename T>
333 T* StatusOr<T>::operator->() {
334   this->EnsureOk();
335   return &this->data_;
336 }
337 
338 template <typename T>
339 const T& StatusOr<T>::operator*() const& {
340   this->EnsureOk();
341   return this->data_;
342 }
343 
344 template <typename T>
345 T& StatusOr<T>::operator*() & {
346   this->EnsureOk();
347   return this->data_;
348 }
349 
350 template <typename T>
351 const T&& StatusOr<T>::operator*() const&& {
352   this->EnsureOk();
353   return std::move(this->data_);
354 }
355 
356 template <typename T>
357 T&& StatusOr<T>::operator*() && {
358   this->EnsureOk();
359   return std::move(this->data_);
360 }
361 
362 template <typename T>
IgnoreError()363 void StatusOr<T>::IgnoreError() const {
364   // no-op
365 }
366 
367 #define TF_ASSERT_OK_AND_ASSIGN(lhs, rexpr)                             \
368   TF_ASSERT_OK_AND_ASSIGN_IMPL(                                         \
369       TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
370       rexpr);
371 
372 #define TF_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr)  \
373   auto statusor = (rexpr);                                  \
374   ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \
375   lhs = std::move(statusor.ValueOrDie())
376 
377 #define TF_STATUS_MACROS_CONCAT_NAME(x, y) TF_STATUS_MACROS_CONCAT_IMPL(x, y)
378 #define TF_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
379 
380 #define TF_ASSIGN_OR_RETURN(lhs, rexpr) \
381   TF_ASSIGN_OR_RETURN_IMPL(             \
382       TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr)
383 
384 #define TF_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \
385   auto statusor = (rexpr);                             \
386   if (TF_PREDICT_FALSE(!statusor.ok())) {              \
387     return statusor.status();                          \
388   }                                                    \
389   lhs = std::move(statusor.ValueOrDie())
390 
391 }  // namespace tensorflow
392 
393 #endif  // TENSORFLOW_CORE_PLATFORM_STATUSOR_H_
394