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