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