• 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_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