#ifndef XRPL_BASICS_EXPECTED_H_INCLUDED #define XRPL_BASICS_EXPECTED_H_INCLUDED #include #include #include namespace ripple { /** Expected is an approximation of std::expected (hoped for in C++23) See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html The implementation is entirely based on boost::outcome_v2::result. */ // Exception thrown by an invalid access to Expected. struct bad_expected_access : public std::runtime_error { bad_expected_access() : runtime_error("bad expected access") { } }; namespace detail { // Custom policy for Expected. Always throw on an invalid access. struct throw_policy : public boost::outcome_v2::policy::base { template static constexpr void wide_value_check(Impl&& self) { if (!base::_has_value(std::forward(self))) Throw(); } template static constexpr void wide_error_check(Impl&& self) { if (!base::_has_error(std::forward(self))) Throw(); } template static constexpr void wide_exception_check(Impl&& self) { if (!base::_has_exception(std::forward(self))) Throw(); } }; } // namespace detail // Definition of Unexpected, which is used to construct the unexpected // return type of an Expected. template class Unexpected { public: static_assert(!std::is_same::value, "E must not be void"); Unexpected() = delete; constexpr explicit Unexpected(E const& e) : val_(e) { } constexpr explicit Unexpected(E&& e) : val_(std::move(e)) { } constexpr E const& value() const& { return val_; } constexpr E& value() & { return val_; } constexpr E&& value() && { return std::move(val_); } constexpr E const&& value() const&& { return std::move(val_); } private: E val_; }; // Unexpected deduction guide that converts array to const*. template Unexpected(E (&)[N]) -> Unexpected; // Definition of Expected. All of the machinery comes from boost::result. template class [[nodiscard]] Expected : private boost::outcome_v2::result { using Base = boost::outcome_v2::result; public: template requires std::convertible_to constexpr Expected(U&& r) : Base(boost::outcome_v2::in_place_type_t{}, std::forward(r)) { } template requires std::convertible_to && (!std::is_reference_v) constexpr Expected(Unexpected e) : Base(boost::outcome_v2::in_place_type_t{}, std::move(e.value())) { } constexpr bool has_value() const { return Base::has_value(); } constexpr T const& value() const { return Base::value(); } constexpr T& value() { return Base::value(); } constexpr E const& error() const { return Base::error(); } constexpr E& error() { return Base::error(); } constexpr explicit operator bool() const { return has_value(); } // Add operator* and operator-> so the Expected API looks a bit more like // what std::expected is likely to look like. See: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html [[nodiscard]] constexpr T& operator*() { return this->value(); } [[nodiscard]] constexpr T const& operator*() const { return this->value(); } [[nodiscard]] constexpr T* operator->() { return &this->value(); } [[nodiscard]] constexpr T const* operator->() const { return &this->value(); } }; // Specialization of Expected. Allows returning either success // (without a value) or the reason for the failure. template class [[nodiscard]] Expected : private boost::outcome_v2::result { using Base = boost::outcome_v2::result; public: // The default constructor makes a successful Expected. // This aligns with std::expected behavior proposed in P0323R10. constexpr Expected() : Base(boost::outcome_v2::success()) { } template requires std::convertible_to && (!std::is_reference_v) constexpr Expected(Unexpected e) : Base(E(std::move(e.value()))) { } constexpr E const& error() const { return Base::error(); } constexpr E& error() { return Base::error(); } constexpr explicit operator bool() const { return Base::has_value(); } }; } // namespace ripple #endif // XRPL_BASICS_EXPECTED_H_INCLUDED