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