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 return ok() ? Status::OK() : std::move(this->status_);
277 }
278
279 template <typename T>
ValueOrDie()280 const T& StatusOr<T>::ValueOrDie() const & {
281 this->EnsureOk();
282 return this->data_;
283 }
284
285 template <typename T>
ValueOrDie()286 T& StatusOr<T>::ValueOrDie() & {
287 this->EnsureOk();
288 return this->data_;
289 }
290
291 template <typename T>
ValueOrDie()292 const T&& StatusOr<T>::ValueOrDie() const && {
293 this->EnsureOk();
294 return std::move(this->data_);
295 }
296
297 template <typename T>
ValueOrDie()298 T&& StatusOr<T>::ValueOrDie() && {
299 this->EnsureOk();
300 return std::move(this->data_);
301 }
302
303 template <typename T>
IgnoreError()304 void StatusOr<T>::IgnoreError() const {
305 // no-op
306 }
307
308 } // namespace port
309 } // namespace stream_executor
310
311 #endif // TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_H_
312