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.value(); |
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.value()); |
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.value()); |
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 tsl::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_TSL_PLATFORM_STATUSOR_H_ |
69 | #define TENSORFLOW_TSL_PLATFORM_STATUSOR_H_ |
70 | |
71 | #include "absl/base/attributes.h" |
72 | #include "tensorflow/tsl/platform/errors.h" |
73 | #include "tensorflow/tsl/platform/macros.h" |
74 | #include "tensorflow/tsl/platform/status.h" |
75 | #include "tensorflow/tsl/platform/statusor_internals.h" |
76 | |
77 | namespace tsl { |
78 | |
79 | #if TF_HAS_CPP_ATTRIBUTE(nodiscard) |
80 | template <typename T> |
81 | class [[nodiscard]] 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 the inner value `T` in-place using the provided args, using the |
131 | // `T(args...)` constructor. |
132 | template <typename... Args> |
133 | explicit StatusOr(absl::in_place_t, Args&&... args); |
134 | |
135 | // Constructs a new StatusOr with the given value. After calling this |
136 | // constructor, calls to value() will succeed, and calls to status() will |
137 | // return OK. |
138 | // |
139 | // NOTE: Not explicit - we want to use StatusOr<T> as a return type |
140 | // so it is convenient and sensible to be able to do 'return T()' |
141 | // when the return type is StatusOr<T>. |
142 | // |
143 | // REQUIRES: T is copy constructible. |
144 | StatusOr(const T& value); |
145 | |
146 | // Constructs a new StatusOr with the given non-ok status. After calling |
147 | // this constructor, calls to value() will CHECK-fail. |
148 | // |
149 | // NOTE: Not explicit - we want to use StatusOr<T> as a return |
150 | // value, so it is convenient and sensible to be able to do 'return |
151 | // Status()' when the return type is StatusOr<T>. |
152 | // |
153 | // REQUIRES: !status.ok(). This requirement is DCHECKed. |
154 | // In optimized builds, passing OkStatus() here will have the effect |
155 | // of passing tsl::error::INTERNAL as a fallback. |
156 | StatusOr(const Status& status); |
157 | StatusOr& operator=(const Status& status); |
158 | |
159 | // TODO(b/62186997): Add operator=(T) overloads. |
160 | |
161 | // Similar to the `const T&` overload. |
162 | // |
163 | // REQUIRES: T is move constructible. |
164 | StatusOr(T&& value); |
165 | |
166 | // RValue versions of the operations declared above. |
167 | StatusOr(Status&& status); |
168 | StatusOr& operator=(Status&& status); |
169 | |
170 | // Returns this->status().ok() |
171 | bool ok() const { return this->status_.ok(); } |
172 | |
173 | // Returns a reference to our status. If this contains a T, then |
174 | // returns OkStatus(). |
175 | const Status& status() const&; |
176 | Status status() &&; |
177 | |
178 | // Returns a reference to our current value, or CHECK-fails if !this->ok(). |
179 | // |
180 | // DEPRECATED: Prefer accessing the value using `operator*` or `operator->` |
181 | // after testing that the StatusOr is OK. If program termination is desired in |
182 | // the case of an error status, consider `CHECK_OK(status_or);`. |
183 | // Note: for value types that are cheap to copy, prefer simple code: |
184 | // |
185 | // T value = statusor.value(); |
186 | // |
187 | // Otherwise, if the value type is expensive to copy, but can be left |
188 | // in the StatusOr, simply assign to a reference: |
189 | // |
190 | // T& value = statusor.value(); // or `const T&` |
191 | // |
192 | // Otherwise, if the value type supports an efficient move, it can be |
193 | // used as follows: |
194 | // |
195 | // T value = std::move(statusor).value(); |
196 | // |
197 | // The std::move on statusor instead of on the whole expression enables |
198 | // warnings about possible uses of the statusor object after the move. |
199 | // C++ style guide waiver for ref-qualified overloads granted in cl/143176389 |
200 | // See go/ref-qualifiers for more details on such overloads. |
201 | const T& value() const&; |
202 | T& value() &; |
203 | const T&& value() const&&; |
204 | T&& value() &&; |
205 | |
206 | // Deprecated, use `value()` instead. |
207 | ABSL_DEPRECATED("Use `value()` instead." ) T& ValueOrDie() &; |
208 | ABSL_DEPRECATED("Use `value()` instead." ) T&& ValueOrDie() &&; |
209 | |
210 | // Returns a reference 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. Alternatively, see value() for a similar API that guarantees |
216 | // CHECK-failing if there is no current value. |
217 | const T& operator*() const&; |
218 | T& operator*() &; |
219 | const T&& operator*() const&&; |
220 | T&& operator*() &&; |
221 | |
222 | // Returns a pointer to the current value. |
223 | // |
224 | // REQUIRES: this->ok() == true, otherwise the behavior is undefined. |
225 | // |
226 | // Use this->ok() or `operator bool()` to verify that there is a current |
227 | // value. |
228 | const T* operator->() const; |
229 | T* operator->(); |
230 | |
231 | // Ignores any errors. This method does nothing except potentially suppress |
232 | // complaints from any tools that are checking that errors are not dropped on |
233 | // the floor. |
234 | void IgnoreError() const; |
235 | }; |
236 | |
237 | //////////////////////////////////////////////////////////////////////////////// |
238 | // Implementation details for StatusOr<T> |
239 | |
240 | template <typename T> |
241 | StatusOr<T>::StatusOr() : Base(Status(tsl::error::UNKNOWN, "" )) {} |
242 | |
243 | template <typename T> |
244 | StatusOr<T>::StatusOr(const T& value) : Base(value) {} |
245 | |
246 | template <typename T> |
247 | StatusOr<T>::StatusOr(const Status& status) : Base(status) {} |
248 | |
249 | template <typename T> |
250 | StatusOr<T>& StatusOr<T>::operator=(const Status& status) { |
251 | this->Assign(status); |
252 | return *this; |
253 | } |
254 | |
255 | template <typename T> |
256 | StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {} |
257 | |
258 | template <typename T> |
259 | template <typename... Args> |
260 | StatusOr<T>::StatusOr(absl::in_place_t, Args&&... args) |
261 | : Base(absl::in_place, std::forward<Args>(args)...) {} |
262 | |
263 | template <typename T> |
264 | StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {} |
265 | |
266 | template <typename T> |
267 | StatusOr<T>& StatusOr<T>::operator=(Status&& status) { |
268 | this->Assign(std::move(status)); |
269 | return *this; |
270 | } |
271 | |
272 | template <typename T> |
273 | template <typename U, |
274 | typename std::enable_if<std::is_convertible<U, T>::value>::type*> |
275 | inline StatusOr<T>::StatusOr(const StatusOr<U>& other) |
276 | : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} |
277 | |
278 | template <typename T> |
279 | template <typename U, |
280 | typename std::enable_if<std::is_convertible<U, T>::value>::type*> |
281 | inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) { |
282 | if (other.ok()) |
283 | this->Assign(other.value()); |
284 | else |
285 | this->Assign(other.status()); |
286 | return *this; |
287 | } |
288 | |
289 | template <typename T> |
290 | template <typename U, |
291 | typename std::enable_if<std::is_convertible<U, T>::value>::type*> |
292 | inline StatusOr<T>::StatusOr(StatusOr<U>&& other) |
293 | : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} |
294 | |
295 | template <typename T> |
296 | template <typename U, |
297 | typename std::enable_if<std::is_convertible<U, T>::value>::type*> |
298 | inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) { |
299 | if (other.ok()) { |
300 | this->Assign(std::move(other).value()); |
301 | } else { |
302 | this->Assign(std::move(other).status()); |
303 | } |
304 | return *this; |
305 | } |
306 | |
307 | template <typename T> |
308 | const Status& StatusOr<T>::status() const& { |
309 | return this->status_; |
310 | } |
311 | template <typename T> |
312 | Status StatusOr<T>::status() && { |
313 | // Note that we copy instead of moving the status here so that |
314 | // ~StatusOrData() can call ok() without invoking UB. |
315 | return ok() ? OkStatus() : this->status_; |
316 | } |
317 | |
318 | template <typename T> |
319 | const T& StatusOr<T>::value() const& { |
320 | this->EnsureOk(); |
321 | return this->data_; |
322 | } |
323 | |
324 | template <typename T> |
325 | T& StatusOr<T>::value() & { |
326 | this->EnsureOk(); |
327 | return this->data_; |
328 | } |
329 | |
330 | template <typename T> |
331 | const T&& StatusOr<T>::value() const&& { |
332 | this->EnsureOk(); |
333 | return std::move(this->data_); |
334 | } |
335 | |
336 | template <typename T> |
337 | T&& StatusOr<T>::value() && { |
338 | this->EnsureOk(); |
339 | return std::move(this->data_); |
340 | } |
341 | |
342 | |
343 | template <typename T> |
344 | T& StatusOr<T>::ValueOrDie() & { |
345 | this->EnsureOk(); |
346 | return this->data_; |
347 | } |
348 | |
349 | |
350 | template <typename T> |
351 | T&& StatusOr<T>::ValueOrDie() && { |
352 | this->EnsureOk(); |
353 | return std::move(this->data_); |
354 | } |
355 | |
356 | template <typename T> |
357 | const T* StatusOr<T>::operator->() const { |
358 | this->EnsureOk(); |
359 | return &this->data_; |
360 | } |
361 | |
362 | template <typename T> |
363 | T* StatusOr<T>::operator->() { |
364 | this->EnsureOk(); |
365 | return &this->data_; |
366 | } |
367 | |
368 | template <typename T> |
369 | const T& StatusOr<T>::operator*() const& { |
370 | this->EnsureOk(); |
371 | return this->data_; |
372 | } |
373 | |
374 | template <typename T> |
375 | T& StatusOr<T>::operator*() & { |
376 | this->EnsureOk(); |
377 | return this->data_; |
378 | } |
379 | |
380 | template <typename T> |
381 | const T&& StatusOr<T>::operator*() const&& { |
382 | this->EnsureOk(); |
383 | return std::move(this->data_); |
384 | } |
385 | |
386 | template <typename T> |
387 | T&& StatusOr<T>::operator*() && { |
388 | this->EnsureOk(); |
389 | return std::move(this->data_); |
390 | } |
391 | |
392 | template <typename T> |
393 | void StatusOr<T>::IgnoreError() const { |
394 | // no-op |
395 | } |
396 | |
397 | #define TF_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ |
398 | TF_ASSERT_OK_AND_ASSIGN_IMPL( \ |
399 | TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \ |
400 | rexpr); |
401 | |
402 | #define TF_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \ |
403 | auto statusor = (rexpr); \ |
404 | ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \ |
405 | lhs = std::move(statusor).value() |
406 | |
407 | #define TF_STATUS_MACROS_CONCAT_NAME(x, y) TF_STATUS_MACROS_CONCAT_IMPL(x, y) |
408 | #define TF_STATUS_MACROS_CONCAT_IMPL(x, y) x##y |
409 | |
410 | #define TF_ASSIGN_OR_RETURN(lhs, rexpr) \ |
411 | TF_ASSIGN_OR_RETURN_IMPL( \ |
412 | TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr) |
413 | |
414 | #define TF_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \ |
415 | auto statusor = (rexpr); \ |
416 | if (TF_PREDICT_FALSE(!statusor.ok())) { \ |
417 | return statusor.status(); \ |
418 | } \ |
419 | lhs = std::move(statusor).value() |
420 | |
421 | } // namespace tsl |
422 | |
423 | #endif // TENSORFLOW_TSL_PLATFORM_STATUSOR_H_ |
424 | |