|
|
|||
File indexing completed on 2025-11-04 09:21:58
0001 // This file is part of the ACTS project. 0002 // 0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project 0004 // 0005 // This Source Code Form is subject to the terms of the Mozilla Public 0006 // License, v. 2.0. If a copy of the MPL was not distributed with this 0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/. 0008 0009 #pragma once 0010 0011 #include <concepts> 0012 #include <optional> 0013 #include <sstream> 0014 #include <system_error> 0015 #include <type_traits> 0016 #include <utility> 0017 #include <variant> 0018 0019 namespace Acts { 0020 0021 /// Class which encapsulates either a valid result, or an error 0022 /// @tparam T The valid result value 0023 /// @tparam E The error, defaults to `std::error_code` 0024 /// 0025 template <typename T, typename E = std::error_code> 0026 class Result { 0027 /// Private constructor which accepts an external variant. 0028 /// This is used by the factory static methods to set up 0029 /// the variant unambiguously in all cases. 0030 explicit Result(std::variant<T, E>&& var) : m_var(std::move(var)) {} 0031 0032 public: 0033 /// Type alias for the value type contained in successful result 0034 using ValueType = T; 0035 /// Type alias for the error type contained in failed result 0036 using ErrorType = E; 0037 0038 /// Default construction is disallowed. 0039 Result() = delete; 0040 0041 /// Copy construction is disallowed 0042 Result(const Result<T, E>& other) = delete; 0043 0044 /// Assignment is disallowed 0045 Result<T, E>& operator=(const Result<T, E>& other) = delete; 0046 0047 /// Move construction is allowed 0048 /// @param other The other result instance to move from 0049 Result(Result<T, E>&& other) noexcept : m_var(std::move(other.m_var)) {} 0050 0051 /// Move assignment is allowed 0052 /// @param other The other result instance, rvalue reference 0053 /// @return The assigned instance 0054 Result<T, E>& operator=(Result<T, E>&& other) noexcept { 0055 m_var = std::move(other.m_var); 0056 return *this; 0057 } 0058 0059 /// @brief Constructor from arbitrary value 0060 /// This constructor allows construction from any value. This constructor is 0061 /// only enabled if T and E are unambiguous, meaning they cannot be implicitly 0062 /// converted and there is T cannot be constructed from E and vice-versa. 0063 /// This means that when this is invoked, the value can be propagated to the 0064 /// underlying variant, and the assignment will be correct, and error will be 0065 /// an error, and a value will be a value. 0066 /// @note If T and E are ambiguous, use the `success` and `failure` static 0067 /// factory methods. 0068 /// @tparam T2 Type of the potential assignment 0069 /// @param value The potential value, could be an actual valid value or an 0070 /// error. 0071 template <typename T2> 0072 Result(T2 value) noexcept // NOLINT(google-explicit-constructor) 0073 // ^ Conversion here is crucial for ergonomics 0074 requires(!std::same_as<T, E> && !std::constructible_from<T, E> && 0075 !std::convertible_to<T, E> && !std::constructible_from<E, T> && 0076 !std::convertible_to<E, T> && 0077 !(std::convertible_to<T2, T> && std::convertible_to<T2, E>)) 0078 : m_var(std::conditional_t<std::is_convertible_v<T2, T>, T, E>{ 0079 std::move(value)}) {} 0080 0081 /// @brief Assignment operator from arbitrary value 0082 /// This operator allows construction from any value. The same rules as for 0083 /// the `Result(T2 value)` constructor apply. 0084 /// * @tparam T2 Type of the potential assignment 0085 /// @param value The potential value, could be an actual valid value or an 0086 /// error. 0087 /// @return The assigned instance 0088 template <typename T2> 0089 Result<T, E>& operator=(T2 value) noexcept 0090 requires(!std::same_as<T, E> && !std::constructible_from<T, E> && 0091 !std::convertible_to<T, E> && !std::constructible_from<E, T> && 0092 !std::convertible_to<E, T> && 0093 !(std::convertible_to<T2, T> && std::convertible_to<T2, E>)) 0094 { 0095 m_var = std::move(std::conditional_t<std::is_convertible_v<T2, T>, T, E>{ 0096 std::move(value)}); 0097 return *this; 0098 } 0099 0100 /// Static helper factory which forces assignment as valid value. 0101 /// @param value The valid value to assign. Will not be converted to E. 0102 /// @return Initialized result object 0103 static Result<T, E> success(T value) { 0104 return Result<T, E>( 0105 std::variant<T, E>{std::in_place_index<0>, std::move(value)}); 0106 } 0107 0108 /// Static helper factory which forces assignment as an error. 0109 /// @param error The error to assign. Will not be converted to T. 0110 /// @return Initialized result object 0111 static Result<T, E> failure(E error) { 0112 return Result<T, E>( 0113 std::variant<T, E>{std::in_place_index<1>, std::move(error)}); 0114 } 0115 0116 /// Checks whether this result contains a valid value, and no error. 0117 /// @return bool Whether result contains an error or not. 0118 bool ok() const noexcept { return m_var.index() == 0; } 0119 0120 /// Returns a reference into the variant to the valid value. 0121 /// @note If `!res.ok()`, this method will abort (noexcept) 0122 /// @return Reference to value stored in the variant. 0123 T& operator*() noexcept { return std::get<T>(m_var); } 0124 0125 /// Returns a reference into the variant to the valid value. 0126 /// @note If `!res.ok()`, this method will abort (noexcept) 0127 /// @return Reference to value stored in the variant. 0128 const T& operator*() const noexcept { return std::get<T>(m_var); } 0129 0130 /// Allows to access members of the stored object with `res->foo` 0131 /// similar to `std::optional`. 0132 /// @note If `!res.ok()`, this method will abort (noexcept) 0133 /// @return Pointer to value stored in the variant. 0134 T* operator->() noexcept { return &std::get<T>(m_var); } 0135 0136 /// Allows to access members of the stored object with `res->foo` 0137 /// similar to `std::optional`. 0138 /// @note If `!res.ok()`, this method will abort (noexcept) 0139 /// @return Pointer to value stored in the variant. 0140 const T* operator->() const noexcept { return &std::get<T>(m_var); } 0141 0142 /// Returns a reference to the error stored in the result. 0143 /// @note If `res.ok()` this method will abort (noexcept) 0144 /// @return Reference to the error 0145 E& error() & noexcept { return std::get<E>(m_var); } 0146 0147 /// Returns a reference to the error stored in the result. 0148 /// @note If `res.ok()` this method will abort (noexcept) 0149 /// @return Reference to the error 0150 const E& error() const& noexcept { return std::get<E>(m_var); } 0151 0152 /// Returns the error by-value. 0153 /// @note If `res.ok()` this method will abort (noexcept) 0154 /// @return The error 0155 E error() && noexcept { return std::move(std::get<E>(m_var)); } 0156 0157 /// Retrieves the valid value from the result object. 0158 /// @note This is the lvalue version, returns a reference to the value 0159 /// @return The valid value as a reference 0160 T& value() & { 0161 checkValueAccess(); 0162 return std::get<T>(m_var); 0163 } 0164 0165 /// Retrieves the valid value from the result object. 0166 /// @note This is the lvalue version, returns a reference to the value 0167 /// @return The valid value as a reference 0168 const T& value() const& { 0169 checkValueAccess(); 0170 return std::get<T>(m_var); 0171 } 0172 0173 /// @cond 0174 /// Retrieves the valid value from the result object. 0175 /// @note This is the rvalue version, returns the value 0176 /// by-value and moves out of the variant. 0177 /// @return The valid value by value (moved) 0178 T value() && { 0179 checkValueAccess(); 0180 return std::move(std::get<T>(m_var)); 0181 } 0182 /// @endcond 0183 0184 /// Retrieves the valid value from the result object, or returns a default 0185 /// value if no valid value exists. 0186 /// 0187 /// @param[in] v The default value to use if no valid value exists. 0188 /// @note This is the lvalue version. 0189 /// @note This function always returns by value. 0190 /// @return Either the valid value, or the given substitute. 0191 template <typename U> 0192 std::conditional_t<std::is_reference_v<U>, const T&, T> value_or(U&& v) const& 0193 requires(std::same_as<std::decay_t<U>, T>) 0194 { 0195 if (ok()) { 0196 return value(); 0197 } else { 0198 return std::forward<U>(v); 0199 } 0200 } 0201 0202 /// @cond 0203 /// Retrieves the valid value from the result object, or returns a default 0204 /// value if no valid value exists. 0205 /// 0206 /// @param[in] v The default value to use if no valid value exists. 0207 /// @return The valid value if present, otherwise the default value 0208 /// @note This is the rvalue version which moves the value out. 0209 /// @note This function always returns by value. 0210 template <typename U> 0211 T value_or(U&& v) && 0212 requires(std::same_as<std::decay_t<U>, T>) 0213 { 0214 if (ok()) { 0215 return std::move(*this).value(); 0216 } else { 0217 return std::forward<U>(v); 0218 } 0219 } 0220 /// @endcond 0221 0222 /// Transforms the value contained in this result. 0223 /// 0224 /// Applying a function `f` to a valid value `x` returns `f(x)`, while 0225 /// applying `f` to an invalid value returns another invalid value. 0226 /// 0227 /// @param[in] callable The transformation function to apply. 0228 /// @note This is the lvalue version. 0229 /// @note This functions is `fmap` on the functor in `A` of `Result<A, E>`. 0230 /// @return The modified valid value if exists, or an error otherwise. 0231 template <typename C> 0232 auto transform(C&& callable) const& 0233 requires std::invocable<C, const T&> 0234 { 0235 using CallableReturnType = decltype(std::declval<C>()(std::declval<T>())); 0236 using R = Result<std::decay_t<CallableReturnType>, E>; 0237 if (ok()) { 0238 return R::success(callable(value())); 0239 } else { 0240 return R::failure(error()); 0241 } 0242 } 0243 0244 /// Transforms the value contained in this result. 0245 /// 0246 /// Applying a function `f` to a valid value `x` returns `f(x)`, while 0247 /// applying `f` to an invalid value returns another invalid value. 0248 /// 0249 /// @param[in] callable The transformation function to apply. 0250 /// @note This is the rvalue version. 0251 /// @note This functions is `fmap` on the functor in `A` of `Result<A, E>`. 0252 /// @return The modified valid value if exists, or an error otherwise. 0253 template <typename C> 0254 auto transform(C&& callable) && 0255 requires std::invocable<C, T&&> 0256 { 0257 using CallableReturnType = decltype(std::declval<C>()(std::declval<T>())); 0258 using R = Result<std::decay_t<CallableReturnType>, E>; 0259 if (ok()) { 0260 return R::success(callable(std::move(*this).value())); 0261 } else { 0262 return R::failure(std::move(*this).error()); 0263 } 0264 } 0265 0266 /// Bind a function to this result monadically. 0267 /// 0268 /// This function takes a function `f` and, if this result contains a valid 0269 /// value `x`, returns `f(x)`. If the type of `x` is `T`, then `f` is 0270 /// expected to accept type `T` and return `Result<U>`. In this case, 0271 /// `transform` would return the unhelpful type `Result<Result<U>>`, so 0272 /// `and_then` strips away the outer layer to return `Result<U>`. If the 0273 /// value is invalid, this returns an invalid value in `Result<U>`. 0274 /// 0275 /// @param[in] callable The transformation function to apply. 0276 /// @note This is the lvalue version. 0277 /// @note This functions is `>>=` on the functor in `A` of `Result<A, E>`. 0278 /// @return The modified valid value if exists, or an error otherwise. 0279 template <typename C> 0280 auto and_then(C&& callable) const& 0281 requires std::invocable<C, const T&> 0282 { 0283 using R = decltype(std::declval<C>()(std::declval<T>())); 0284 0285 static_assert(std::same_as<typename R::ErrorType, ErrorType>, 0286 "bind must take a callable with the same error type"); 0287 0288 if (ok()) { 0289 return callable(value()); 0290 } else { 0291 return R::failure(error()); 0292 } 0293 } 0294 0295 /// Bind a function to this result monadically. 0296 /// 0297 /// This function takes a function `f` and, if this result contains a valid 0298 /// value `x`, returns `f(x)`. If the type of `x` is `T`, then `f` is 0299 /// expected to accept type `T` and return `Result<U>`. In this case, 0300 /// `transform` would return the unhelpful type `Result<Result<U>>`, so 0301 /// `and_then` strips away the outer layer to return `Result<U>`. If the 0302 /// value is invalid, this returns an invalid value in `Result<U>`. 0303 /// 0304 /// @param[in] callable The transformation function to apply. 0305 /// @note This is the rvalue version. 0306 /// @note This functions is `>>=` on the functor in `A` of `Result<A, E>`. 0307 /// @return The modified valid value if exists, or an error otherwise. 0308 template <typename C> 0309 auto and_then(C&& callable) && 0310 requires std::invocable<C, T&&> 0311 { 0312 using R = decltype(std::declval<C>()(std::declval<T>())); 0313 0314 static_assert(std::same_as<typename R::ErrorType, ErrorType>, 0315 "bind must take a callable with the same error type"); 0316 0317 if (ok()) { 0318 return callable(std::move(*this).value()); 0319 } else { 0320 return R::failure(std::move(*this).error()); 0321 } 0322 } 0323 0324 private: 0325 std::variant<T, E> m_var; 0326 0327 void checkValueAccess() const { 0328 if (m_var.index() != 0) { 0329 if constexpr (std::is_same_v<E, std::error_code>) { 0330 std::stringstream ss; 0331 const auto& e = std::get<E>(m_var); 0332 ss << "Value called on error value: " << e.category().name() << ": " 0333 << e.message() << " [" << e.value() << "]"; 0334 throw std::runtime_error(ss.str()); 0335 } else { 0336 throw std::runtime_error("Value called on error value"); 0337 } 0338 } 0339 } 0340 }; 0341 0342 /// Template specialization for the void case. 0343 /// This specialization handles the case where there is no actual return value, 0344 /// but 0345 /// an error might be returned. Returning the error directly would make handling 0346 /// different from other functions using the `Result<T, E>` mechanism. 0347 /// `Result<void, E>` does not have the dereference operator, and value methods. 0348 /// The static `success` factory does not accept a value. 0349 /// @note To ease usage, this `Result<void, E>` is default constructible in the 0350 /// *ok* 0351 /// state, whereas `Result<T, E>` is not. 0352 /// @tparam E The type of the error 0353 /// @{ 0354 template <typename E> 0355 class Result<void, E> { 0356 public: 0357 /// Type alias for the value type (void) in successful result 0358 using ValueType = void; 0359 /// Type alias for the error type contained in failed result 0360 using ErrorType = E; 0361 0362 /// Default constructor which initializes the result in the ok state. 0363 Result() = default; 0364 0365 /// The copy constructor is deleted. 0366 /// @param other The other result instance to copy from 0367 Result(const Result<void, E>& other) = default; 0368 0369 /// The (self) assignment operator is deleted. 0370 /// @param other The other result instance to assign from 0371 /// @return Reference to this result instance 0372 Result<void, E>& operator=(const Result<void, E>& other) = default; 0373 0374 /// Move constructor 0375 /// @param other The other result object, rvalue ref 0376 Result(Result<void, E>&& other) noexcept : m_opt(std::move(other.m_opt)) {} 0377 0378 /// Move assignment operator 0379 /// @param other The other result object, rvalue ref 0380 /// @return Reference to this result for assignment chaining 0381 Result<void, E>& operator=(Result<void, E>&& other) noexcept { 0382 m_opt = std::move(other.m_opt); 0383 return *this; 0384 } 0385 0386 /// Constructor from error. This implicitly requires E2 to be convertible to 0387 /// E. 0388 /// @tparam E2 The type of the actual error 0389 /// @param error The instance of the actual error 0390 template <typename E2> 0391 Result(E2 error) noexcept // NOLINT(google-explicit-constructor) 0392 // ^ Conversion here is crucial for ergonomics 0393 : m_opt(std::move(error)) {} 0394 0395 /// Assignment operator from an error. 0396 /// @tparam E2 The type of the actual error 0397 /// @param error The instance of the actual error 0398 /// @return The assigned instance 0399 template <typename E2> 0400 Result<void, E>& operator=(E2 error) { 0401 m_opt = std::move(error); 0402 return *this; 0403 } 0404 0405 /// Static factory function to initialize the result in the ok state. 0406 /// @return Result object, in ok state 0407 static Result<void, E> success() { return Result<void, E>(); } 0408 0409 /// Static factory function to initialize the result in the error state. 0410 /// @param error The error to initialize with. 0411 /// @return Result object, in error state. 0412 static Result<void, E> failure(E error) { 0413 return Result<void, E>(std::move(error)); 0414 } 0415 0416 /// Checks whether this result is in the ok state, and no error. 0417 /// @return bool Whether result contains an error or not. 0418 bool ok() const noexcept { return !m_opt; } 0419 0420 /// Returns a reference to the error stored in the result. 0421 /// @note If `res.ok()` this method will abort (noexcept) 0422 /// @return Reference to the error 0423 E& error() & noexcept { return m_opt.value(); } 0424 0425 /// Returns a reference to the error stored in the result. 0426 /// @note If `res.ok()` this method will abort (noexcept) 0427 /// @return Reference to the error 0428 const E& error() const& noexcept { return m_opt.value(); } 0429 0430 /// Returns the error by-value. 0431 /// @note If `res.ok()` this method will abort (noexcept) 0432 /// @return The error by value 0433 E error() && noexcept { return std::move(m_opt.value()); } 0434 0435 /// Validates this void result and throws if an error is present 0436 /// 0437 /// This method checks if the result contains an error and throws an exception 0438 /// if one is found. For void results, there is no value to return - this 0439 /// method only performs validation. 0440 /// 0441 /// @throws std::runtime_error if the result contains an error 0442 void value() const { checkValueAccess(); } 0443 0444 private: 0445 std::optional<E> m_opt; 0446 0447 void checkValueAccess() const { 0448 if (m_opt.has_value()) { 0449 if constexpr (std::is_same_v<E, std::error_code>) { 0450 std::stringstream ss; 0451 const auto& e = m_opt.value(); 0452 ss << "Value called on error value: " << e.category().name() << ": " 0453 << e.message() << " [" << e.value() << "]"; 0454 throw std::runtime_error(ss.str()); 0455 } else { 0456 throw std::runtime_error("Value called on error value"); 0457 } 0458 } 0459 } 0460 }; 0461 /// @} 0462 0463 } // namespace Acts
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|