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
17 // object. StatusOr models the concept of an object that is either a
18 // usable value, or an error Status explaining why such a value is
19 // not present. To this end, StatusOr<T> does not allow its Status
20 // value to be Status::OK. Furthermore, the value of a StatusOr<T*>
21 // must not be null. This is enforced by a debug check in most cases,
22 // but even when it is not, clients must not set the value to null.
23 //
24 // The primary use-case for StatusOr<T> is as the return value of a
25 // function which may fail.
26 //
27 // Example client usage for a StatusOr<T>, where T is not a pointer:
28 //
29 // StatusOr<float> result = DoBigCalculationThatCouldFail();
30 // if (result.ok()) {
31 // float answer = result.ValueOrDie();
32 // printf("Big calculation yielded: %f", answer);
33 // } else {
34 // LOG(ERROR) << result.status();
35 // }
36 //
37 // Example client usage for a StatusOr<T*>:
38 //
39 // StatusOr<Foo*> result = FooFactory::MakeNewFoo(arg);
40 // if (result.ok()) {
41 // std::unique_ptr<Foo> foo(result.ValueOrDie());
42 // foo->DoSomethingCool();
43 // } else {
44 // LOG(ERROR) << result.status();
45 // }
46 //
47 // Example client usage for a StatusOr<std::unique_ptr<T>>:
48 //
49 // StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
50 // if (result.ok()) {
51 // std::unique_ptr<Foo> foo = std::move(result.ValueOrDie());
52 // foo->DoSomethingCool();
53 // } else {
54 // LOG(ERROR) << result.status();
55 // }
56 //
57 // Example factory implementation returning StatusOr<T*>:
58 //
59 // StatusOr<Foo*> FooFactory::MakeNewFoo(int arg) {
60 // if (arg <= 0) {
61 // return tensorflow::InvalidArgument("Arg must be positive");
62 // } else {
63 // return new Foo(arg);
64 // }
65 // }
66 //
67 // Note that the assignment operators require that destroying the currently
68 // stored value cannot invalidate the argument; in other words, the argument
69 // cannot be an alias for the current value, or anything owned by the current
70 // value.
71 #ifndef TENSORFLOW_COMPILER_XLA_STATUSOR_H_
72 #define TENSORFLOW_COMPILER_XLA_STATUSOR_H_
73
74 #include "tensorflow/compiler/xla/status.h"
75 #include "tensorflow/compiler/xla/statusor_internals.h"
76 #include "tensorflow/core/platform/macros.h"
77
78 namespace xla {
79
80 #if defined(__clang__)
81 // Only clang supports warn_unused_result as a type annotation.
82 template <typename T>
83 class TF_MUST_USE_RESULT StatusOr;
84 #endif
85
86 template <typename T>
87 class StatusOr : private internal_statusor::StatusOrData<T>,
88 private internal_statusor::TraitsBase<
89 std::is_copy_constructible<T>::value,
90 std::is_move_constructible<T>::value> {
91 template <typename U>
92 friend class StatusOr;
93
94 typedef internal_statusor::StatusOrData<T> Base;
95
96 public:
97 typedef T element_type;
98
99 // Constructs a new StatusOr with Status::UNKNOWN status. This is marked
100 // 'explicit' to try to catch cases like 'return {};', where people think
101 // StatusOr<std::vector<int>> will be initialized with an empty vector,
102 // instead of a Status::UNKNOWN status.
103 explicit StatusOr();
104
105 // StatusOr<T> will be copy constructible/assignable if T is copy
106 // constructible.
107 StatusOr(const StatusOr&) = default;
108 StatusOr& operator=(const StatusOr&) = default;
109
110 // StatusOr<T> will be move constructible/assignable if T is move
111 // constructible.
112 StatusOr(StatusOr&&) = default;
113 StatusOr& operator=(StatusOr&&) = default;
114
115 // Conversion copy/move constructor, T must be convertible from U.
116 // TODO(b/62186717): These should not participate in overload resolution if U
117 // is not convertible to T.
118 template <typename U>
119 StatusOr(const StatusOr<U>& other);
120 template <typename U>
121 StatusOr(StatusOr<U>&& other);
122
123 // Conversion copy/move assignment operator, T must be convertible from U.
124 template <typename U>
125 StatusOr& operator=(const StatusOr<U>& other);
126 template <typename U>
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>
StatusOr(const StatusOr<U> & other)237 inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
238 : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {}
239
240 template <typename T>
241 template <typename U>
242 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
243 if (other.ok())
244 this->Assign(other.ValueOrDie());
245 else
246 this->Assign(other.status());
247 return *this;
248 }
249
250 template <typename T>
251 template <typename U>
StatusOr(StatusOr<U> && other)252 inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
253 : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {}
254
255 template <typename T>
256 template <typename U>
257 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
258 if (other.ok()) {
259 this->Assign(std::move(other).ValueOrDie());
260 } else {
261 this->Assign(std::move(other).status());
262 }
263 return *this;
264 }
265
266 template <typename T>
status()267 const Status& StatusOr<T>::status() const & {
268 return this->status_;
269 }
270 template <typename T>
status()271 Status StatusOr<T>::status() && {
272 return ok() ? Status::OK() : std::move(this->status_);
273 }
274
275 template <typename T>
ValueOrDie()276 const T& StatusOr<T>::ValueOrDie() const & {
277 this->EnsureOk();
278 return this->data_;
279 }
280
281 template <typename T>
ValueOrDie()282 T& StatusOr<T>::ValueOrDie() & {
283 this->EnsureOk();
284 return this->data_;
285 }
286
287 template <typename T>
ValueOrDie()288 const T&& StatusOr<T>::ValueOrDie() const && {
289 this->EnsureOk();
290 return std::move(this->data_);
291 }
292
293 template <typename T>
ValueOrDie()294 T&& StatusOr<T>::ValueOrDie() && {
295 this->EnsureOk();
296 return std::move(this->data_);
297 }
298
299 template <typename T>
IgnoreError()300 void StatusOr<T>::IgnoreError() const {
301 // no-op
302 }
303
304 } // namespace xla
305
306 #endif // TENSORFLOW_COMPILER_XLA_STATUSOR_H_
307