![]() |
|
|||
File indexing completed on 2025-08-27 08:47:19
0001 // 0002 // Copyright 2017 Asylo authors 0003 // 0004 // Licensed under the Apache License, Version 2.0 (the "License"); 0005 // you may not use this file except in compliance with the License. 0006 // You may obtain a copy of the License at 0007 // 0008 // http://www.apache.org/licenses/LICENSE-2.0 0009 // 0010 // Unless required by applicable law or agreed to in writing, software 0011 // distributed under the License is distributed on an "AS IS" BASIS, 0012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0013 // See the License for the specific language governing permissions and 0014 // limitations under the License. 0015 // 0016 0017 // Adapted from Asylo 0018 0019 #pragma once 0020 0021 #include <cstddef> 0022 #include <new> 0023 #include <string> 0024 #include <type_traits> 0025 #include <utility> 0026 0027 #include "arrow/status.h" 0028 #include "arrow/util/aligned_storage.h" 0029 #include "arrow/util/compare.h" 0030 0031 namespace arrow { 0032 0033 template <typename> 0034 struct EnsureResult; 0035 0036 namespace internal { 0037 0038 ARROW_EXPORT void DieWithMessage(const std::string& msg); 0039 0040 ARROW_EXPORT void InvalidValueOrDie(const Status& st); 0041 0042 ARROW_EXPORT Status UninitializedResult(); 0043 0044 } // namespace internal 0045 0046 /// A class for representing either a usable value, or an error. 0047 /// 0048 /// A Result object either contains a value of type `T` or a Status object 0049 /// explaining why such a value is not present. The type `T` must be 0050 /// copy-constructible and/or move-constructible. 0051 /// 0052 /// The state of a Result object may be determined by calling ok() or 0053 /// status(). The ok() method returns true if the object contains a valid value. 0054 /// The status() method returns the internal Status object. A Result object 0055 /// that contains a valid value will return an OK Status for a call to status(). 0056 /// 0057 /// A value of type `T` may be extracted from a Result object through a call 0058 /// to ValueOrDie(). This function should only be called if a call to ok() 0059 /// returns true. Sample usage: 0060 /// 0061 /// ``` 0062 /// arrow::Result<Foo> result = CalculateFoo(); 0063 /// if (result.ok()) { 0064 /// Foo foo = result.ValueOrDie(); 0065 /// foo.DoSomethingCool(); 0066 /// } else { 0067 /// ARROW_LOG(ERROR) << result.status(); 0068 /// } 0069 /// ``` 0070 /// 0071 /// If `T` is a move-only type, like `std::unique_ptr<>`, then the value should 0072 /// only be extracted after invoking `std::move()` on the Result object. 0073 /// Sample usage: 0074 /// 0075 /// ``` 0076 /// arrow::Result<std::unique_ptr<Foo>> result = CalculateFoo(); 0077 /// if (result.ok()) { 0078 /// std::unique_ptr<Foo> foo = std::move(result).ValueOrDie(); 0079 /// foo->DoSomethingCool(); 0080 /// } else { 0081 /// ARROW_LOG(ERROR) << result.status(); 0082 /// } 0083 /// ``` 0084 /// 0085 /// Result is provided for the convenience of implementing functions that 0086 /// return some value but may fail during execution. For instance, consider a 0087 /// function with the following signature: 0088 /// 0089 /// ``` 0090 /// arrow::Status CalculateFoo(int *output); 0091 /// ``` 0092 /// 0093 /// This function may instead be written as: 0094 /// 0095 /// ``` 0096 /// arrow::Result<int> CalculateFoo(); 0097 /// ``` 0098 template <class T> 0099 class [[nodiscard]] Result : public util::EqualityComparable<Result<T>> { 0100 template <typename U> 0101 friend class Result; 0102 0103 static_assert(!std::is_same<T, Status>::value, 0104 "this assert indicates you have probably made a metaprogramming error"); 0105 0106 public: 0107 using ValueType = T; 0108 0109 /// Constructs a Result object that contains a non-OK status. 0110 /// 0111 /// This constructor is marked `explicit` to prevent attempts to `return {}` 0112 /// from a function with a return type of, for example, 0113 /// `Result<std::vector<int>>`. While `return {}` seems like it would return 0114 /// an empty vector, it will actually invoke the default constructor of 0115 /// Result. 0116 explicit Result() noexcept // NOLINT(runtime/explicit) 0117 : status_(internal::UninitializedResult()) {} 0118 0119 ~Result() noexcept { Destroy(); } 0120 0121 /// Constructs a Result object with the given non-OK Status object. All 0122 /// calls to ValueOrDie() on this object will abort. The given `status` must 0123 /// not be an OK status, otherwise this constructor will abort. 0124 /// 0125 /// This constructor is not declared explicit so that a function with a return 0126 /// type of `Result<T>` can return a Status object, and the status will be 0127 /// implicitly converted to the appropriate return type as a matter of 0128 /// convenience. 0129 /// 0130 /// \param status The non-OK Status object to initialize to. 0131 Result(const Status& status) noexcept // NOLINT(runtime/explicit) 0132 : status_(status) { 0133 if (ARROW_PREDICT_FALSE(status.ok())) { 0134 internal::DieWithMessage(std::string("Constructed with a non-error status: ") + 0135 status.ToString()); 0136 } 0137 } 0138 0139 /// Constructs a Result object that contains `value`. The resulting object 0140 /// is considered to have an OK status. The wrapped element can be accessed 0141 /// with ValueOrDie(). 0142 /// 0143 /// This constructor is made implicit so that a function with a return type of 0144 /// `Result<T>` can return an object of type `U &&`, implicitly converting 0145 /// it to a `Result<T>` object. 0146 /// 0147 /// Note that `T` must be implicitly constructible from `U`, and `U` must not 0148 /// be a (cv-qualified) Status or Status-reference type. Due to C++ 0149 /// reference-collapsing rules and perfect-forwarding semantics, this 0150 /// constructor matches invocations that pass `value` either as a const 0151 /// reference or as an rvalue reference. Since Result needs to work for both 0152 /// reference and rvalue-reference types, the constructor uses perfect 0153 /// forwarding to avoid invalidating arguments that were passed by reference. 0154 /// See http://thbecker.net/articles/rvalue_references/section_08.html for 0155 /// additional details. 0156 /// 0157 /// \param value The value to initialize to. 0158 template <typename U, 0159 typename E = typename std::enable_if< 0160 std::is_constructible<T, U>::value && std::is_convertible<U, T>::value && 0161 !std::is_same<typename std::remove_reference< 0162 typename std::remove_cv<U>::type>::type, 0163 Status>::value>::type> 0164 Result(U&& value) noexcept { // NOLINT(runtime/explicit) 0165 ConstructValue(std::forward<U>(value)); 0166 } 0167 0168 /// Constructs a Result object that contains `value`. The resulting object 0169 /// is considered to have an OK status. The wrapped element can be accessed 0170 /// with ValueOrDie(). 0171 /// 0172 /// This constructor is made implicit so that a function with a return type of 0173 /// `Result<T>` can return an object of type `T`, implicitly converting 0174 /// it to a `Result<T>` object. 0175 /// 0176 /// \param value The value to initialize to. 0177 // NOTE `Result(U&& value)` above should be sufficient, but some compilers 0178 // fail matching it. 0179 Result(T&& value) noexcept { // NOLINT(runtime/explicit) 0180 ConstructValue(std::move(value)); 0181 } 0182 0183 /// Copy constructor. 0184 /// 0185 /// This constructor needs to be explicitly defined because the presence of 0186 /// the move-assignment operator deletes the default copy constructor. In such 0187 /// a scenario, since the deleted copy constructor has stricter binding rules 0188 /// than the templated copy constructor, the templated constructor cannot act 0189 /// as a copy constructor, and any attempt to copy-construct a `Result` 0190 /// object results in a compilation error. 0191 /// 0192 /// \param other The value to copy from. 0193 Result(const Result& other) noexcept : status_(other.status_) { 0194 if (ARROW_PREDICT_TRUE(status_.ok())) { 0195 ConstructValue(other.ValueUnsafe()); 0196 } 0197 } 0198 0199 /// Templatized constructor that constructs a `Result<T>` from a const 0200 /// reference to a `Result<U>`. 0201 /// 0202 /// `T` must be implicitly constructible from `const U &`. 0203 /// 0204 /// \param other The value to copy from. 0205 template <typename U, typename E = typename std::enable_if< 0206 std::is_constructible<T, const U&>::value && 0207 std::is_convertible<U, T>::value>::type> 0208 Result(const Result<U>& other) noexcept : status_(other.status_) { 0209 if (ARROW_PREDICT_TRUE(status_.ok())) { 0210 ConstructValue(other.ValueUnsafe()); 0211 } 0212 } 0213 0214 /// Copy-assignment operator. 0215 /// 0216 /// \param other The Result object to copy. 0217 Result& operator=(const Result& other) noexcept { 0218 // Check for self-assignment. 0219 if (ARROW_PREDICT_FALSE(this == &other)) { 0220 return *this; 0221 } 0222 Destroy(); 0223 status_ = other.status_; 0224 if (ARROW_PREDICT_TRUE(status_.ok())) { 0225 ConstructValue(other.ValueUnsafe()); 0226 } 0227 return *this; 0228 } 0229 0230 /// Templatized constructor which constructs a `Result<T>` by moving the 0231 /// contents of a `Result<U>`. `T` must be implicitly constructible from `U 0232 /// &&`. 0233 /// 0234 /// Sets `other` to contain a non-OK status with a`StatusError::Invalid` 0235 /// error code. 0236 /// 0237 /// \param other The Result object to move from and set to a non-OK status. 0238 template <typename U, 0239 typename E = typename std::enable_if<std::is_constructible<T, U&&>::value && 0240 std::is_convertible<U, T>::value>::type> 0241 Result(Result<U>&& other) noexcept { 0242 if (ARROW_PREDICT_TRUE(other.status_.ok())) { 0243 status_ = std::move(other.status_); 0244 ConstructValue(other.MoveValueUnsafe()); 0245 } else { 0246 // If we moved the status, the other status may become ok but the other 0247 // value hasn't been constructed => crash on other destructor. 0248 status_ = other.status_; 0249 } 0250 } 0251 0252 /// Move-assignment operator. 0253 /// 0254 /// Sets `other` to an invalid state.. 0255 /// 0256 /// \param other The Result object to assign from and set to a non-OK 0257 /// status. 0258 Result& operator=(Result&& other) noexcept { 0259 // Check for self-assignment. 0260 if (ARROW_PREDICT_FALSE(this == &other)) { 0261 return *this; 0262 } 0263 Destroy(); 0264 if (ARROW_PREDICT_TRUE(other.status_.ok())) { 0265 status_ = std::move(other.status_); 0266 ConstructValue(other.MoveValueUnsafe()); 0267 } else { 0268 // If we moved the status, the other status may become ok but the other 0269 // value hasn't been constructed => crash on other destructor. 0270 status_ = other.status_; 0271 } 0272 return *this; 0273 } 0274 0275 /// Compare to another Result. 0276 bool Equals(const Result& other) const { 0277 if (ARROW_PREDICT_TRUE(status_.ok())) { 0278 return other.status_.ok() && ValueUnsafe() == other.ValueUnsafe(); 0279 } 0280 return status_ == other.status_; 0281 } 0282 0283 /// Indicates whether the object contains a `T` value. Generally instead 0284 /// of accessing this directly you will want to use ASSIGN_OR_RAISE defined 0285 /// below. 0286 /// 0287 /// \return True if this Result object's status is OK (i.e. a call to ok() 0288 /// returns true). If this function returns true, then it is safe to access 0289 /// the wrapped element through a call to ValueOrDie(). 0290 constexpr bool ok() const { return status_.ok(); } 0291 0292 /// \brief Equivalent to ok(). 0293 // operator bool() const { return ok(); } 0294 0295 /// Gets the stored status object, or an OK status if a `T` value is stored. 0296 /// 0297 /// \return The stored non-OK status object, or an OK status if this object 0298 /// has a value. 0299 constexpr const Status& status() const& { return status_; } 0300 0301 /// Gets the stored status object, or an OK status if a `T` value is stored. 0302 /// 0303 /// \return The stored non-OK status object, or an OK status if this object 0304 /// has a value. 0305 Status status() && { 0306 if (ARROW_PREDICT_TRUE(ok())) return Status::OK(); 0307 auto tmp = internal::UninitializedResult(); 0308 std::swap(status_, tmp); 0309 return tmp; 0310 } 0311 0312 /// Gets the stored `T` value. 0313 /// 0314 /// This method should only be called if this Result object's status is OK 0315 /// (i.e. a call to ok() returns true), otherwise this call will abort. 0316 /// 0317 /// \return The stored `T` value. 0318 const T& ValueOrDie() const& { 0319 if (ARROW_PREDICT_FALSE(!ok())) { 0320 internal::InvalidValueOrDie(status_); 0321 } 0322 return ValueUnsafe(); 0323 } 0324 const T& operator*() const& { return ValueOrDie(); } 0325 const T* operator->() const { return &ValueOrDie(); } 0326 0327 /// Gets a mutable reference to the stored `T` value. 0328 /// 0329 /// This method should only be called if this Result object's status is OK 0330 /// (i.e. a call to ok() returns true), otherwise this call will abort. 0331 /// 0332 /// \return The stored `T` value. 0333 T& ValueOrDie() & { 0334 if (ARROW_PREDICT_FALSE(!ok())) { 0335 internal::InvalidValueOrDie(status_); 0336 } 0337 return ValueUnsafe(); 0338 } 0339 T& operator*() & { return ValueOrDie(); } 0340 T* operator->() { return &ValueOrDie(); } 0341 0342 /// Moves and returns the internally-stored `T` value. 0343 /// 0344 /// This method should only be called if this Result object's status is OK 0345 /// (i.e. a call to ok() returns true), otherwise this call will abort. The 0346 /// Result object is invalidated after this call and will be updated to 0347 /// contain a non-OK status. 0348 /// 0349 /// \return The stored `T` value. 0350 T ValueOrDie() && { 0351 if (ARROW_PREDICT_FALSE(!ok())) { 0352 internal::InvalidValueOrDie(status_); 0353 } 0354 return MoveValueUnsafe(); 0355 } 0356 T operator*() && { return std::move(*this).ValueOrDie(); } 0357 0358 /// Helper method for implementing Status returning functions in terms of semantically 0359 /// equivalent Result returning functions. For example: 0360 /// 0361 /// Status GetInt(int *out) { return GetInt().Value(out); } 0362 template <typename U, typename E = typename std::enable_if< 0363 std::is_constructible<U, T>::value>::type> 0364 Status Value(U* out) && { 0365 if (!ok()) { 0366 return std::move(*this).status(); 0367 } 0368 *out = U(MoveValueUnsafe()); 0369 return Status::OK(); 0370 } 0371 0372 /// Move and return the internally stored value or alternative if an error is stored. 0373 T ValueOr(T alternative) && { 0374 if (!ok()) { 0375 return alternative; 0376 } 0377 return MoveValueUnsafe(); 0378 } 0379 0380 /// Retrieve the value if ok(), falling back to an alternative generated by the provided 0381 /// factory 0382 template <typename G> 0383 T ValueOrElse(G&& generate_alternative) && { 0384 if (ok()) { 0385 return MoveValueUnsafe(); 0386 } 0387 return std::forward<G>(generate_alternative)(); 0388 } 0389 0390 /// Apply a function to the internally stored value to produce a new result or propagate 0391 /// the stored error. 0392 template <typename M> 0393 typename EnsureResult<decltype(std::declval<M&&>()(std::declval<T&&>()))>::type Map( 0394 M&& m) && { 0395 if (!ok()) { 0396 return std::move(*this).status(); 0397 } 0398 return std::forward<M>(m)(MoveValueUnsafe()); 0399 } 0400 0401 /// Apply a function to the internally stored value to produce a new result or propagate 0402 /// the stored error. 0403 template <typename M> 0404 typename EnsureResult<decltype(std::declval<M&&>()(std::declval<const T&>()))>::type 0405 Map(M&& m) const& { 0406 if (!ok()) { 0407 return status(); 0408 } 0409 return std::forward<M>(m)(ValueUnsafe()); 0410 } 0411 0412 /// Cast the internally stored value to produce a new result or propagate the stored 0413 /// error. 0414 template <typename U, typename E = typename std::enable_if< 0415 std::is_constructible<U, T>::value>::type> 0416 Result<U> As() && { 0417 if (!ok()) { 0418 return std::move(*this).status(); 0419 } 0420 return U(MoveValueUnsafe()); 0421 } 0422 0423 /// Cast the internally stored value to produce a new result or propagate the stored 0424 /// error. 0425 template <typename U, typename E = typename std::enable_if< 0426 std::is_constructible<U, const T&>::value>::type> 0427 Result<U> As() const& { 0428 if (!ok()) { 0429 return status(); 0430 } 0431 return U(ValueUnsafe()); 0432 } 0433 0434 constexpr const T& ValueUnsafe() const& { return *storage_.get(); } 0435 0436 constexpr T& ValueUnsafe() & { return *storage_.get(); } 0437 0438 T ValueUnsafe() && { return MoveValueUnsafe(); } 0439 0440 T MoveValueUnsafe() { return std::move(*storage_.get()); } 0441 0442 private: 0443 Status status_; // pointer-sized 0444 internal::AlignedStorage<T> storage_; 0445 0446 template <typename U> 0447 void ConstructValue(U&& u) noexcept { 0448 storage_.construct(std::forward<U>(u)); 0449 } 0450 0451 void Destroy() noexcept { 0452 if (ARROW_PREDICT_TRUE(status_.ok())) { 0453 static_assert(offsetof(Result<T>, status_) == 0, 0454 "Status is guaranteed to be at the start of Result<>"); 0455 storage_.destroy(); 0456 } 0457 } 0458 }; 0459 0460 #define ARROW_ASSIGN_OR_RAISE_IMPL(result_name, lhs, rexpr) \ 0461 auto&& result_name = (rexpr); \ 0462 ARROW_RETURN_IF_(!(result_name).ok(), (result_name).status(), ARROW_STRINGIFY(rexpr)); \ 0463 lhs = std::move(result_name).ValueUnsafe(); 0464 0465 #define ARROW_ASSIGN_OR_RAISE_NAME(x, y) ARROW_CONCAT(x, y) 0466 0467 /// \brief Execute an expression that returns a Result, extracting its value 0468 /// into the variable defined by `lhs` (or returning a Status on error). 0469 /// 0470 /// Example: Assigning to a new value: 0471 /// ARROW_ASSIGN_OR_RAISE(auto value, MaybeGetValue(arg)); 0472 /// 0473 /// Example: Assigning to an existing value: 0474 /// ValueType value; 0475 /// ARROW_ASSIGN_OR_RAISE(value, MaybeGetValue(arg)); 0476 /// 0477 /// WARNING: ARROW_ASSIGN_OR_RAISE expands into multiple statements; 0478 /// it cannot be used in a single statement (e.g. as the body of an if 0479 /// statement without {})! 0480 /// 0481 /// WARNING: ARROW_ASSIGN_OR_RAISE `std::move`s its right operand. If you have 0482 /// an lvalue Result which you *don't* want to move out of cast appropriately. 0483 /// 0484 /// WARNING: ARROW_ASSIGN_OR_RAISE is not a single expression; it will not 0485 /// maintain lifetimes of all temporaries in `rexpr` (e.g. 0486 /// `ARROW_ASSIGN_OR_RAISE(auto x, MakeTemp().GetResultRef());` 0487 /// will most likely segfault)! 0488 #define ARROW_ASSIGN_OR_RAISE(lhs, rexpr) \ 0489 ARROW_ASSIGN_OR_RAISE_IMPL(ARROW_ASSIGN_OR_RAISE_NAME(_error_or_value, __COUNTER__), \ 0490 lhs, rexpr); 0491 0492 namespace internal { 0493 0494 template <typename T> 0495 inline const Status& GenericToStatus(const Result<T>& res) { 0496 return res.status(); 0497 } 0498 0499 template <typename T> 0500 inline Status GenericToStatus(Result<T>&& res) { 0501 return std::move(res).status(); 0502 } 0503 0504 } // namespace internal 0505 0506 template <typename T, typename R = typename EnsureResult<T>::type> 0507 R ToResult(T t) { 0508 return R(std::move(t)); 0509 } 0510 0511 template <typename T> 0512 struct EnsureResult { 0513 using type = Result<T>; 0514 }; 0515 0516 template <typename T> 0517 struct EnsureResult<Result<T>> { 0518 using type = Result<T>; 0519 }; 0520 0521 } // namespace arrow
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
![]() ![]() |