Back to home page

EIC code displayed by LXR

 
 

    


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