1 //
2 // Copyright 2017 Asylo authors
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 // Adapted from Asylo
18
19 #pragma once
20
21 #include <new>
22 #include <string>
23 #include <type_traits>
24 #include <utility>
25
26 #include "arrow/status.h"
27 #include "arrow/util/compare.h"
28
29 namespace arrow {
30
31 namespace internal {
32
33 #if __cplusplus >= 201703L
34 using std::launder;
35 #else
36 template <class T>
37 constexpr T* launder(T* p) noexcept {
38 return p;
39 }
40 #endif
41
42 ARROW_EXPORT void DieWithMessage(const std::string& msg);
43
44 ARROW_EXPORT void InvalidValueOrDie(const Status& st);
45
46 } // namespace internal
47
48 /// A class for representing either a usable value, or an error.
49 ///
50 /// A Result object either contains a value of type `T` or a Status object
51 /// explaining why such a value is not present. The type `T` must be
52 /// copy-constructible and/or move-constructible.
53 ///
54 /// The state of a Result object may be determined by calling ok() or
55 /// status(). The ok() method returns true if the object contains a valid value.
56 /// The status() method returns the internal Status object. A Result object
57 /// that contains a valid value will return an OK Status for a call to status().
58 ///
59 /// A value of type `T` may be extracted from a Result object through a call
60 /// to ValueOrDie(). This function should only be called if a call to ok()
61 /// returns true. Sample usage:
62 ///
63 /// ```
64 /// arrow::Result<Foo> result = CalculateFoo();
65 /// if (result.ok()) {
66 /// Foo foo = result.ValueOrDie();
67 /// foo.DoSomethingCool();
68 /// } else {
69 /// ARROW_LOG(ERROR) << result.status();
70 /// }
71 /// ```
72 ///
73 /// If `T` is a move-only type, like `std::unique_ptr<>`, then the value should
74 /// only be extracted after invoking `std::move()` on the Result object.
75 /// Sample usage:
76 ///
77 /// ```
78 /// arrow::Result<std::unique_ptr<Foo>> result = CalculateFoo();
79 /// if (result.ok()) {
80 /// std::unique_ptr<Foo> foo = std::move(result).ValueOrDie();
81 /// foo->DoSomethingCool();
82 /// } else {
83 /// ARROW_LOG(ERROR) << result.status();
84 /// }
85 /// ```
86 ///
87 /// Result is provided for the convenience of implementing functions that
88 /// return some value but may fail during execution. For instance, consider a
89 /// function with the following signature:
90 ///
91 /// ```
92 /// arrow::Status CalculateFoo(int *output);
93 /// ```
94 ///
95 /// This function may instead be written as:
96 ///
97 /// ```
98 /// arrow::Result<int> CalculateFoo();
99 /// ```
100 template <class T>
101 class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable<Result<T>> {
102 template <typename U>
103 friend class Result;
104
105 static_assert(!std::is_same<T, Status>::value,
106 "this assert indicates you have probably made a metaprogramming error");
107
108 public:
109 using ValueType = T;
110
111 /// Constructs a Result object that contains a non-OK status.
112 ///
113 /// This constructor is marked `explicit` to prevent attempts to `return {}`
114 /// from a function with a return type of, for example,
115 /// `Result<std::vector<int>>`. While `return {}` seems like it would return
116 /// an empty vector, it will actually invoke the default constructor of
117 /// Result.
Result()118 explicit Result() // NOLINT(runtime/explicit)
119 : status_(Status::UnknownError("Uninitialized Result<T>")) {}
120
~Result()121 ~Result() noexcept { Destroy(); }
122
123 /// Constructs a Result object with the given non-OK Status object. All
124 /// calls to ValueOrDie() on this object will abort. The given `status` must
125 /// not be an OK status, otherwise this constructor will abort.
126 ///
127 /// This constructor is not declared explicit so that a function with a return
128 /// type of `Result<T>` can return a Status object, and the status will be
129 /// implicitly converted to the appropriate return type as a matter of
130 /// convenience.
131 ///
132 /// \param status The non-OK Status object to initialize to.
Result(const Status & status)133 Result(const Status& status) // NOLINT(runtime/explicit)
134 : status_(status) {
135 if (ARROW_PREDICT_FALSE(status.ok())) {
136 internal::DieWithMessage(std::string("Constructed with a non-error status: ") +
137 status.ToString());
138 }
139 }
140
141 /// Constructs a Result object that contains `value`. The resulting object
142 /// is considered to have an OK status. The wrapped element can be accessed
143 /// with ValueOrDie().
144 ///
145 /// This constructor is made implicit so that a function with a return type of
146 /// `Result<T>` can return an object of type `U &&`, implicitly converting
147 /// it to a `Result<T>` object.
148 ///
149 /// Note that `T` must be implicitly constructible from `U`, and `U` must not
150 /// be a (cv-qualified) Status or Status-reference type. Due to C++
151 /// reference-collapsing rules and perfect-forwarding semantics, this
152 /// constructor matches invocations that pass `value` either as a const
153 /// reference or as an rvalue reference. Since Result needs to work for both
154 /// reference and rvalue-reference types, the constructor uses perfect
155 /// forwarding to avoid invalidating arguments that were passed by reference.
156 /// See http://thbecker.net/articles/rvalue_references/section_08.html for
157 /// additional details.
158 ///
159 /// \param value The value to initialize to.
160 template <typename U,
161 typename E = typename std::enable_if<
162 std::is_constructible<T, U>::value && std::is_convertible<U, T>::value &&
163 !std::is_same<typename std::remove_reference<
164 typename std::remove_cv<U>::type>::type,
165 Status>::value>::type>
Result(U && value)166 Result(U&& value) noexcept { // NOLINT(runtime/explicit)
167 ConstructValue(std::forward<U>(value));
168 }
169
170 /// Constructs a Result object that contains `value`. The resulting object
171 /// is considered to have an OK status. The wrapped element can be accessed
172 /// with ValueOrDie().
173 ///
174 /// This constructor is made implicit so that a function with a return type of
175 /// `Result<T>` can return an object of type `T`, implicitly converting
176 /// it to a `Result<T>` object.
177 ///
178 /// \param value The value to initialize to.
179 // NOTE `Result(U&& value)` above should be sufficient, but some compilers
180 // fail matching it.
Result(T && value)181 Result(T&& value) noexcept { // NOLINT(runtime/explicit)
182 ConstructValue(std::move(value));
183 }
184
185 /// Copy constructor.
186 ///
187 /// This constructor needs to be explicitly defined because the presence of
188 /// the move-assignment operator deletes the default copy constructor. In such
189 /// a scenario, since the deleted copy constructor has stricter binding rules
190 /// than the templated copy constructor, the templated constructor cannot act
191 /// as a copy constructor, and any attempt to copy-construct a `Result`
192 /// object results in a compilation error.
193 ///
194 /// \param other The value to copy from.
Result(const Result & other)195 Result(const Result& other) : status_(other.status_) {
196 if (ARROW_PREDICT_TRUE(status_.ok())) {
197 ConstructValue(other.ValueUnsafe());
198 }
199 }
200
201 /// Templatized constructor that constructs a `Result<T>` from a const
202 /// reference to a `Result<U>`.
203 ///
204 /// `T` must be implicitly constructible from `const U &`.
205 ///
206 /// \param other The value to copy from.
207 template <typename U, typename E = typename std::enable_if<
208 std::is_constructible<T, const U&>::value &&
209 std::is_convertible<U, T>::value>::type>
Result(const Result<U> & other)210 Result(const Result<U>& other) : status_(other.status_) {
211 if (ARROW_PREDICT_TRUE(status_.ok())) {
212 ConstructValue(other.ValueUnsafe());
213 }
214 }
215
216 /// Copy-assignment operator.
217 ///
218 /// \param other The Result object to copy.
219 Result& operator=(const Result& other) {
220 // Check for self-assignment.
221 if (this == &other) {
222 return *this;
223 }
224 Destroy();
225 status_ = other.status_;
226 if (ARROW_PREDICT_TRUE(status_.ok())) {
227 ConstructValue(other.ValueUnsafe());
228 }
229 return *this;
230 }
231
232 /// Templatized constructor which constructs a `Result<T>` by moving the
233 /// contents of a `Result<U>`. `T` must be implicitly constructible from `U
234 /// &&`.
235 ///
236 /// Sets `other` to contain a non-OK status with a`StatusError::Invalid`
237 /// error code.
238 ///
239 /// \param other The Result object to move from and set to a non-OK status.
240 template <typename U,
241 typename E = typename std::enable_if<std::is_constructible<T, U&&>::value &&
242 std::is_convertible<U, T>::value>::type>
Result(Result<U> && other)243 Result(Result<U>&& other) noexcept {
244 if (ARROW_PREDICT_TRUE(other.status_.ok())) {
245 status_ = std::move(other.status_);
246 ConstructValue(other.MoveValueUnsafe());
247 } else {
248 // If we moved the status, the other status may become ok but the other
249 // value hasn't been constructed => crash on other destructor.
250 status_ = other.status_;
251 }
252 }
253
254 /// Move-assignment operator.
255 ///
256 /// Sets `other` to an invalid state..
257 ///
258 /// \param other The Result object to assign from and set to a non-OK
259 /// status.
260 Result& operator=(Result&& other) noexcept {
261 // Check for self-assignment.
262 if (this == &other) {
263 return *this;
264 }
265 Destroy();
266 if (ARROW_PREDICT_TRUE(other.status_.ok())) {
267 status_ = std::move(other.status_);
268 ConstructValue(other.MoveValueUnsafe());
269 } else {
270 // If we moved the status, the other status may become ok but the other
271 // value hasn't been constructed => crash on other destructor.
272 status_ = other.status_;
273 }
274 return *this;
275 }
276
277 /// Compare to another Result.
Equals(const Result & other)278 bool Equals(const Result& other) const {
279 if (ARROW_PREDICT_TRUE(status_.ok())) {
280 return other.status_.ok() && ValueUnsafe() == other.ValueUnsafe();
281 }
282 return status_ == other.status_;
283 }
284
285 /// Indicates whether the object contains a `T` value. Generally instead
286 /// of accessing this directly you will want to use ASSIGN_OR_RAISE defined
287 /// below.
288 ///
289 /// \return True if this Result object's status is OK (i.e. a call to ok()
290 /// returns true). If this function returns true, then it is safe to access
291 /// the wrapped element through a call to ValueOrDie().
ok()292 bool ok() const { return status_.ok(); }
293
294 /// \brief Equivalent to ok().
295 // operator bool() const { return ok(); }
296
297 /// Gets the stored status object, or an OK status if a `T` value is stored.
298 ///
299 /// \return The stored non-OK status object, or an OK status if this object
300 /// has a value.
status()301 Status status() const { return status_; }
302
303 /// Gets the stored `T` value.
304 ///
305 /// This method should only be called if this Result object's status is OK
306 /// (i.e. a call to ok() returns true), otherwise this call will abort.
307 ///
308 /// \return The stored `T` value.
ValueOrDie()309 const T& ValueOrDie() const& {
310 if (ARROW_PREDICT_FALSE(!ok())) {
311 internal::InvalidValueOrDie(status_);
312 }
313 return ValueUnsafe();
314 }
315 const T& operator*() const& { return ValueOrDie(); }
316
317 /// Gets a mutable reference to the stored `T` value.
318 ///
319 /// This method should only be called if this Result object's status is OK
320 /// (i.e. a call to ok() returns true), otherwise this call will abort.
321 ///
322 /// \return The stored `T` value.
ValueOrDie()323 T& ValueOrDie() & {
324 if (ARROW_PREDICT_FALSE(!ok())) {
325 internal::InvalidValueOrDie(status_);
326 }
327 return ValueUnsafe();
328 }
329 T& operator*() & { return ValueOrDie(); }
330
331 /// Moves and returns the internally-stored `T` value.
332 ///
333 /// This method should only be called if this Result object's status is OK
334 /// (i.e. a call to ok() returns true), otherwise this call will abort. The
335 /// Result object is invalidated after this call and will be updated to
336 /// contain a non-OK status.
337 ///
338 /// \return The stored `T` value.
ValueOrDie()339 T ValueOrDie() && {
340 if (ARROW_PREDICT_FALSE(!ok())) {
341 internal::InvalidValueOrDie(status_);
342 }
343 return MoveValueUnsafe();
344 }
345 T operator*() && { return std::move(*this).ValueOrDie(); }
346
347 /// Helper method for implementing Status returning functions in terms of semantically
348 /// equivalent Result returning functions. For example:
349 ///
350 /// Status GetInt(int *out) { return GetInt().Value(out); }
351 template <typename U, typename E = typename std::enable_if<
352 std::is_constructible<U, T>::value>::type>
Value(U * out)353 Status Value(U* out) && {
354 if (!ok()) {
355 return status();
356 }
357 *out = U(MoveValueUnsafe());
358 return Status::OK();
359 }
360
361 /// Move and return the internally stored value or alternative if an error is stored.
ValueOr(T alternative)362 T ValueOr(T alternative) && {
363 if (!ok()) {
364 return alternative;
365 }
366 return MoveValueUnsafe();
367 }
368
369 /// Retrieve the value if ok(), falling back to an alternative generated by the provided
370 /// factory
371 template <typename G>
ValueOrElse(G && generate_alternative)372 T ValueOrElse(G&& generate_alternative) && {
373 if (ok()) {
374 return MoveValueUnsafe();
375 }
376 return generate_alternative();
377 }
378
379 /// Apply a function to the internally stored value to produce a new result or propagate
380 /// the stored error.
381 template <typename M>
Map(M && m)382 typename std::result_of<M && (T)>::type Map(M&& m) && {
383 if (!ok()) {
384 return status();
385 }
386 return std::forward<M>(m)(MoveValueUnsafe());
387 }
388
389 /// Apply a function to the internally stored value to produce a new result or propagate
390 /// the stored error.
391 template <typename M>
Map(M && m)392 typename std::result_of<M && (const T&)>::type Map(M&& m) const& {
393 if (!ok()) {
394 return status();
395 }
396 return std::forward<M>(m)(ValueUnsafe());
397 }
398
ValueUnsafe()399 const T& ValueUnsafe() const& {
400 return *internal::launder(reinterpret_cast<const T*>(&data_));
401 }
402
ValueUnsafe()403 T& ValueUnsafe() & { return *internal::launder(reinterpret_cast<T*>(&data_)); }
404
ValueUnsafe()405 T ValueUnsafe() && { return MoveValueUnsafe(); }
406
MoveValueUnsafe()407 T MoveValueUnsafe() {
408 return std::move(*internal::launder(reinterpret_cast<T*>(&data_)));
409 }
410
411 private:
412 Status status_; // pointer-sized
413 typename std::aligned_storage<sizeof(T), alignof(T)>::type data_;
414
415 template <typename U>
ConstructValue(U && u)416 void ConstructValue(U&& u) {
417 new (&data_) T(std::forward<U>(u));
418 }
419
Destroy()420 void Destroy() {
421 if (ARROW_PREDICT_TRUE(status_.ok())) {
422 internal::launder(reinterpret_cast<const T*>(&data_))->~T();
423 }
424 }
425 };
426
427 #define ARROW_ASSIGN_OR_RAISE_IMPL(result_name, lhs, rexpr) \
428 auto result_name = (rexpr); \
429 ARROW_RETURN_NOT_OK((result_name).status()); \
430 lhs = std::move(result_name).MoveValueUnsafe();
431
432 #define ARROW_ASSIGN_OR_RAISE_NAME(x, y) ARROW_CONCAT(x, y)
433
434 /// \brief Execute an expression that returns a Result, extracting its value
435 /// into the variable defined by `lhs` (or returning a Status on error).
436 ///
437 /// Example: Assigning to a new value:
438 /// ARROW_ASSIGN_OR_RAISE(auto value, MaybeGetValue(arg));
439 ///
440 /// Example: Assigning to an existing value:
441 /// ValueType value;
442 /// ARROW_ASSIGN_OR_RAISE(value, MaybeGetValue(arg));
443 ///
444 /// WARNING: ARROW_ASSIGN_OR_RAISE expands into multiple statements;
445 /// it cannot be used in a single statement (e.g. as the body of an if
446 /// statement without {})!
447 #define ARROW_ASSIGN_OR_RAISE(lhs, rexpr) \
448 ARROW_ASSIGN_OR_RAISE_IMPL(ARROW_ASSIGN_OR_RAISE_NAME(_error_or_value, __COUNTER__), \
449 lhs, rexpr);
450
451 namespace internal {
452
453 template <typename T>
GenericToStatus(const Result<T> & res)454 inline Status GenericToStatus(const Result<T>& res) {
455 return res.status();
456 }
457
458 template <typename T>
GenericToStatus(Result<T> && res)459 inline Status GenericToStatus(Result<T>&& res) {
460 return std::move(res).status();
461 }
462
463 } // namespace internal
464
465 } // namespace arrow
466