Back to home page

sPhenix code displayed by LXR

 
 

    


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