1/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations 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
77namespace tsl {
78
79#if TF_HAS_CPP_ATTRIBUTE(nodiscard)
80template <typename T>
81class [[nodiscard]] StatusOr;
82#endif
83
84template <typename T>
85class 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
240template <typename T>
241StatusOr<T>::StatusOr() : Base(Status(tsl::error::UNKNOWN, "")) {}
242
243template <typename T>
244StatusOr<T>::StatusOr(const T& value) : Base(value) {}
245
246template <typename T>
247StatusOr<T>::StatusOr(const Status& status) : Base(status) {}
248
249template <typename T>
250StatusOr<T>& StatusOr<T>::operator=(const Status& status) {
251 this->Assign(status);
252 return *this;
253}
254
255template <typename T>
256StatusOr<T>::StatusOr(T&& value) : Base(std::move(value)) {}
257
258template <typename T>
259template <typename... Args>
260StatusOr<T>::StatusOr(absl::in_place_t, Args&&... args)
261 : Base(absl::in_place, std::forward<Args>(args)...) {}
262
263template <typename T>
264StatusOr<T>::StatusOr(Status&& status) : Base(std::move(status)) {}
265
266template <typename T>
267StatusOr<T>& StatusOr<T>::operator=(Status&& status) {
268 this->Assign(std::move(status));
269 return *this;
270}
271
272template <typename T>
273template <typename U,
274 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
275inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
276 : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
277
278template <typename T>
279template <typename U,
280 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
281inline 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
289template <typename T>
290template <typename U,
291 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
292inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
293 : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
294
295template <typename T>
296template <typename U,
297 typename std::enable_if<std::is_convertible<U, T>::value>::type*>
298inline 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
307template <typename T>
308const Status& StatusOr<T>::status() const& {
309 return this->status_;
310}
311template <typename T>
312Status 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
318template <typename T>
319const T& StatusOr<T>::value() const& {
320 this->EnsureOk();
321 return this->data_;
322}
323
324template <typename T>
325T& StatusOr<T>::value() & {
326 this->EnsureOk();
327 return this->data_;
328}
329
330template <typename T>
331const T&& StatusOr<T>::value() const&& {
332 this->EnsureOk();
333 return std::move(this->data_);
334}
335
336template <typename T>
337T&& StatusOr<T>::value() && {
338 this->EnsureOk();
339 return std::move(this->data_);
340}
341
342
343template <typename T>
344T& StatusOr<T>::ValueOrDie() & {
345 this->EnsureOk();
346 return this->data_;
347}
348
349
350template <typename T>
351T&& StatusOr<T>::ValueOrDie() && {
352 this->EnsureOk();
353 return std::move(this->data_);
354}
355
356template <typename T>
357const T* StatusOr<T>::operator->() const {
358 this->EnsureOk();
359 return &this->data_;
360}
361
362template <typename T>
363T* StatusOr<T>::operator->() {
364 this->EnsureOk();
365 return &this->data_;
366}
367
368template <typename T>
369const T& StatusOr<T>::operator*() const& {
370 this->EnsureOk();
371 return this->data_;
372}
373
374template <typename T>
375T& StatusOr<T>::operator*() & {
376 this->EnsureOk();
377 return this->data_;
378}
379
380template <typename T>
381const T&& StatusOr<T>::operator*() const&& {
382 this->EnsureOk();
383 return std::move(this->data_);
384}
385
386template <typename T>
387T&& StatusOr<T>::operator*() && {
388 this->EnsureOk();
389 return std::move(this->data_);
390}
391
392template <typename T>
393void 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