![]() |
|
|||
File indexing completed on 2025-08-05 08:09:30
0001 // This file is part of the Acts project. 0002 // 0003 // Copyright (C) 2019 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 http://mozilla.org/MPL/2.0/. 0008 0009 #pragma once 0010 0011 #include <optional> 0012 #include <sstream> 0013 #include <system_error> 0014 #include <type_traits> 0015 #include <utility> 0016 #include <variant> 0017 0018 namespace Acts { 0019 0020 /// Class which encapsulates either a valid result, or an error 0021 /// @tparam T The valid result value 0022 /// @tparam E The error, defaults to `std::error_code` 0023 /// 0024 template <typename T, typename E = std::error_code> 0025 class Result { 0026 /// Private constructor which accepts an external variant. 0027 /// This is used by the factory static methods to set up 0028 /// the variant unambiguously in all cases. 0029 Result(std::variant<T, E>&& var) : m_var(std::move(var)) {} 0030 0031 public: 0032 /// Default construction is disallowed. 0033 Result() = delete; 0034 0035 /// Copy construction is disallowed 0036 Result(const Result<T, E>& other) = delete; 0037 0038 /// Assignment is disallowed 0039 Result<T, E>& operator=(const Result<T, E>& other) = delete; 0040 0041 /// Move construction is allowed 0042 Result(Result<T, E>&& other) : m_var(std::move(other.m_var)) {} 0043 0044 /// Move assignment is allowed 0045 /// @param other The other result instance, rvalue reference 0046 /// @return The assigned instance 0047 Result<T, E>& operator=(Result<T, E>&& other) { 0048 m_var = std::move(other.m_var); 0049 return *this; 0050 } 0051 0052 /// @brief Constructor from arbitrary value 0053 /// This constructor allows construction from any value. This constructor is 0054 /// only enabled if T and E are unambiguous, meaning they cannot be implicitly 0055 /// converted and there is T cannot be constructed from E and vice-versa. 0056 /// This means that when this is invoked, the value can be propagated to the 0057 /// underlying variant, and the assignment will be correct, and error will be 0058 /// an error, and a value will be a value. 0059 /// @note If T and E are ambiguous, use the `success` and `failure` static 0060 /// factory methods. 0061 /// @tparam T2 Type of the potential assignment 0062 /// @param value The potential value, could be an actual valid value or an 0063 /// error. 0064 template < 0065 typename T2, typename _E = E, typename _T = T, 0066 typename = std::enable_if_t< 0067 (!std::is_same_v<_T, _E> && !std::is_constructible_v<_T, _E> && 0068 !std::is_convertible_v<_T, _E> && !std::is_constructible_v<_E, _T> && 0069 !std::is_convertible_v<_E, _T> && 0070 !(std::is_convertible_v<T2, _T> && std::is_convertible_v<T2, _E>))>> 0071 Result(T2 value) noexcept 0072 : m_var(std::conditional_t<std::is_convertible_v<T2, _T>, T, E>{ 0073 std::move(value)}) {} 0074 0075 /// @brief Assignment operator from arbitrary value 0076 /// This operator allows construction from any value. The same rules as for 0077 /// the `Result(T2 value)` constructor apply. 0078 /// * @tparam T2 Type of the potential assignment 0079 /// @param value The potential value, could be an actual valid value or an 0080 /// error. 0081 /// @return The assigned instance 0082 template < 0083 typename T2, typename _E = E, typename _T = T, 0084 typename = std::enable_if_t< 0085 (!std::is_same_v<_T, _E> && !std::is_constructible_v<_T, _E> && 0086 !std::is_convertible_v<_T, _E> && !std::is_constructible_v<_E, _T> && 0087 !std::is_convertible_v<_E, _T> && 0088 !(std::is_convertible_v<T2, _T> && std::is_convertible_v<T2, _E>))>> 0089 Result<T, E>& operator=(T2 value) noexcept { 0090 m_var = std::move(std::conditional_t<std::is_convertible_v<T2, _T>, T, E>{ 0091 std::move(value)}); 0092 return *this; 0093 } 0094 0095 /// Static helper factory which forces assignment as valid value. 0096 /// @param value The valid value to assign. Will not be converted to E. 0097 /// @return Initialized result object 0098 static Result<T, E> success(T value) { 0099 return Result<T, E>( 0100 std::variant<T, E>{std::in_place_index<0>, std::move(value)}); 0101 } 0102 0103 /// Static helper factory which forces assignment as an error. 0104 /// @param error The error to assign. Will not be converted to T. 0105 /// @return Initialized result object 0106 static Result<T, E> failure(E error) { 0107 return Result<T, E>( 0108 std::variant<T, E>{std::in_place_index<1>, std::move(error)}); 0109 } 0110 0111 /// Checks whether this result contains a valid value, and no error. 0112 /// @return bool Whether result contains an error or not. 0113 bool ok() const noexcept { return m_var.index() == 0; } 0114 0115 /// Returns a reference into the variant to the valid value. 0116 /// @note If `!res.ok()`, this method will abort (noexcept) 0117 /// @return Reference to value stored in the variant. 0118 T& operator*() noexcept { return std::get<T>(m_var); } 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 const T& operator*() const noexcept { return std::get<T>(m_var); } 0124 0125 /// Allows to access members of the stored object with `res->foo` 0126 /// similar to `std::optional`. 0127 /// @note If `!res.ok()`, this method will abort (noexcept) 0128 /// @return Pointer to value stored in the variant. 0129 T* operator->() noexcept { return &std::get<T>(m_var); } 0130 0131 /// Allows to access members of the stored object with `res->foo` 0132 /// similar to `std::optional`. 0133 /// @note If `!res.ok()`, this method will abort (noexcept) 0134 /// @return Pointer to value stored in the variant. 0135 const T* operator->() const noexcept { return &std::get<T>(m_var); } 0136 0137 /// Returns a reference to the error stored in the result. 0138 /// @note If `res.ok()` this method will abort (noexcept) 0139 /// @return Reference to the error 0140 E& error() & noexcept { return std::get<E>(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 const E& error() const& noexcept { return std::get<E>(m_var); } 0146 0147 /// Returns the error by-value. 0148 /// @note If `res.ok()` this method will abort (noexcept) 0149 /// @return The error 0150 E error() && noexcept { return std::move(std::get<E>(m_var)); } 0151 0152 /// Retrieves the valid value from the result object. 0153 /// @note This is the lvalue version, returns a reference to the value 0154 /// @return The valid value as a reference 0155 T& value() & { 0156 checkValueAccess(); 0157 return std::get<T>(m_var); 0158 } 0159 0160 /// Retrieves the valid value from the result object. 0161 /// @note This is the lvalue version, returns a reference to the value 0162 /// @return The valid value as a reference 0163 const T& value() const& { 0164 checkValueAccess(); 0165 return std::get<T>(m_var); 0166 } 0167 0168 /// Retrieves the valid value from the result object. 0169 /// @note This is the rvalue version, returns the value 0170 /// by-value and moves out of the variant. 0171 /// @return The valid value by value, moved out of the variant. 0172 T value() && { 0173 checkValueAccess(); 0174 return std::move(std::get<T>(m_var)); 0175 } 0176 0177 private: 0178 std::variant<T, E> m_var; 0179 0180 void checkValueAccess() const { 0181 if (m_var.index() != 0) { 0182 if constexpr (std::is_same_v<E, std::error_code>) { 0183 std::stringstream ss; 0184 const auto& e = std::get<E>(m_var); 0185 ss << "Value called on error value: " << e.category().name() << ": " 0186 << e.message() << " [" << e.value() << "]"; 0187 throw std::runtime_error(ss.str()); 0188 } else { 0189 throw std::runtime_error("Value called on error value"); 0190 } 0191 } 0192 } 0193 }; 0194 0195 /// Template specialization for the void case. 0196 /// This specialization handles the case where there is no actual return value, 0197 /// but 0198 /// an error might be returned. Returning the error directly would make handling 0199 /// different from other functions using the `Result<T, E>` mechanism. 0200 /// `Result<void, E>` does not have the dereference operator, and value methods. 0201 /// The static `success` factory does not accept a value. 0202 /// @note To ease usage, this `Result<void, E>` is default constructible in the 0203 /// *ok* 0204 /// state, whereas `Result<T, E>` is not. 0205 /// @tparam E The type of the error 0206 /// 0207 template <typename E> 0208 class Result<void, E> { 0209 public: 0210 /// Default constructor which initializes the result in the ok state. 0211 Result() = default; 0212 0213 /// The copy constructor is deleted. 0214 Result(const Result<void, E>& other) = default; 0215 0216 /// The (self) assignment operator is deleted. 0217 Result<void, E>& operator=(const Result<void, E>& other) = default; 0218 0219 /// Move constructor 0220 /// @param other The other result object, rvalue ref 0221 Result(Result<void, E>&& other) : m_opt(std::move(other.m_opt)) {} 0222 0223 /// Move assignment operator 0224 /// @param other The other result object, rvalue ref 0225 Result<void, E>& operator=(Result<void, E>&& other) noexcept { 0226 m_opt = std::move(other.m_opt); 0227 return *this; 0228 } 0229 0230 /// Constructor from error. This implicitly requires E2 to be convertible to 0231 /// E. 0232 /// @tparam E2 The type of the actual error 0233 /// @param error The instance of the actual error 0234 template <typename E2> 0235 Result(E2 error) noexcept : m_opt(std::move(error)) {} 0236 0237 /// Assignment operator from an error. 0238 /// @tparam E2 The type of the actual error 0239 /// @param error The instance of the actual error 0240 /// @return The assigned instance 0241 template <typename E2> 0242 Result<void, E>& operator=(E2 error) { 0243 m_opt = std::move(error); 0244 return *this; 0245 } 0246 0247 /// Static factory function to initialize the result in the ok state. 0248 /// @return Result object, in ok state 0249 static Result<void, E> success() { return Result<void, E>(); } 0250 0251 /// Static factory function to initialize the result in the error state. 0252 /// @param error The error to initialize with. 0253 /// @return Result object, in error state. 0254 static Result<void, E> failure(E error) { 0255 return Result<void, E>(std::move(error)); 0256 } 0257 0258 /// Checks whether this result is in the ok state, and no error. 0259 /// @return bool Whether result contains an error or not. 0260 bool ok() const noexcept { return !m_opt; } 0261 0262 /// Returns a reference to the error stored in the result. 0263 /// @note If `res.ok()` this method will abort (noexcept) 0264 /// @return Reference to the error 0265 E& error() & noexcept { return m_opt.value(); } 0266 0267 /// Returns a reference to the error stored in the result. 0268 /// @note If `res.ok()` this method will abort (noexcept) 0269 /// @return Reference to the error 0270 const E& error() const& noexcept { return m_opt.value(); } 0271 0272 /// Returns the error by-value. 0273 /// @note If `res.ok()` this method will abort (noexcept) 0274 /// @return Reference to the error 0275 E error() && noexcept { return std::move(m_opt.value()); } 0276 0277 private: 0278 std::optional<E> m_opt; 0279 }; 0280 0281 } // 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 |
![]() ![]() |