• 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;
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 
ConsumeValueOrDie()197   T ConsumeValueOrDie() { return std::move(ValueOrDie()); }
198 
199   // Ignores any errors. This method does nothing except potentially suppress
200   // complaints from any tools that are checking that errors are not dropped on
201   // the floor.
202   void IgnoreError() const;
203 };
204 
205 ////////////////////////////////////////////////////////////////////////////////
206 // Implementation details for StatusOr<T>
207 
208 template <typename T>
StatusOr()209 StatusOr<T>::StatusOr() : Base(Status(tensorflow::error::UNKNOWN, "")) {}
210 
211 template <typename T>
StatusOr(const T & value)212 StatusOr<T>::StatusOr(const T& value) : Base(value) {}
213 
214 template <typename T>
StatusOr(const Status & status)215 StatusOr<T>::StatusOr(const Status& status) : Base(status) {}
216 
217 template <typename T>
218 StatusOr<T>& StatusOr<T>::operator=(const Status& status) {
219   this->Assign(status);
220   return *this;
221 }
222 
223 template <typename T>
StatusOr(T && value)224 StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {}
225 
226 template <typename T>
StatusOr(Status && status)227 StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {}
228 
229 template <typename T>
230 StatusOr<T>& StatusOr<T>::operator=(Status&& status) {
231   this->Assign(std::move(status));
232   return *this;
233 }
234 
235 template <typename T>
236 template <typename U,
237           typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(const StatusOr<U> & other)238 inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
239     : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
240 
241 template <typename T>
242 template <typename U,
243           typename std::enable_if<std::is_convertible<U, T>::value>::type*>
244 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
245   if (other.ok())
246     this->Assign(other.ValueOrDie());
247   else
248     this->Assign(other.status());
249   return *this;
250 }
251 
252 template <typename T>
253 template <typename U,
254           typename std::enable_if<std::is_convertible<U, T>::value>::type*>
StatusOr(StatusOr<U> && other)255 inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
256     : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
257 
258 template <typename T>
259 template <typename U,
260           typename std::enable_if<std::is_convertible<U, T>::value>::type*>
261 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
262   if (other.ok()) {
263     this->Assign(std::move(other).ValueOrDie());
264   } else {
265     this->Assign(std::move(other).status());
266   }
267   return *this;
268 }
269 
270 template <typename T>
status()271 const Status& StatusOr<T>::status() const & {
272   return this->status_;
273 }
274 template <typename T>
status()275 Status StatusOr<T>::status() && {
276   // Note that we copy instead of moving the status here so that
277   // ~StatusOrData() can call ok() without invoking UB.
278   return ok() ? Status::OK() : this->status_;
279 }
280 
281 template <typename T>
ValueOrDie()282 const T& StatusOr<T>::ValueOrDie() const & {
283   this->EnsureOk();
284   return this->data_;
285 }
286 
287 template <typename T>
ValueOrDie()288 T& StatusOr<T>::ValueOrDie() & {
289   this->EnsureOk();
290   return this->data_;
291 }
292 
293 template <typename T>
ValueOrDie()294 const T&& StatusOr<T>::ValueOrDie() const && {
295   this->EnsureOk();
296   return std::move(this->data_);
297 }
298 
299 template <typename T>
ValueOrDie()300 T&& StatusOr<T>::ValueOrDie() && {
301   this->EnsureOk();
302   return std::move(this->data_);
303 }
304 
305 template <typename T>
IgnoreError()306 void StatusOr<T>::IgnoreError() const {
307   // no-op
308 }
309 
310 }  // namespace port
311 
312 #define TF_ASSERT_OK_AND_ASSIGN(lhs, rexpr)                             \
313   TF_ASSERT_OK_AND_ASSIGN_IMPL(                                         \
314       TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
315       rexpr);
316 
317 #define TF_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr)  \
318   auto statusor = (rexpr);                                  \
319   ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \
320   lhs = std::move(statusor.ValueOrDie())
321 
322 #define TF_STATUS_MACROS_CONCAT_NAME(x, y) TF_STATUS_MACROS_CONCAT_IMPL(x, y)
323 #define TF_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
324 
325 #define TF_ASSIGN_OR_RETURN(lhs, rexpr) \
326   TF_ASSIGN_OR_RETURN_IMPL(             \
327       TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr)
328 
329 #define TF_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \
330   auto statusor = (rexpr);                             \
331   if (TF_PREDICT_FALSE(!statusor.ok())) {              \
332     return statusor.status();                          \
333   }                                                    \
334   lhs = std::move(statusor.ValueOrDie())
335 
336 }  // namespace stream_executor
337 
338 #endif  // TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_H_
339