Back to home page

EIC code displayed by LXR

 
 

    


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