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