Back to home page

EIC code displayed by LXR

 
 

    


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