mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 10:35:50 +00:00
Per XLS-0095, we are taking steps to rename ripple(d) to xrpl(d). This change specifically removes all copyright notices referencing Ripple, XRPLF, and certain affiliated contributors upon mutual agreement, so the notice in the LICENSE.md file applies throughout. Copyright notices referencing external contributions remain as-is. Duplicate verbiage is also removed.
238 lines
5.0 KiB
C++
238 lines
5.0 KiB
C++
#ifndef XRPL_BASICS_EXPECTED_H_INCLUDED
|
|
#define XRPL_BASICS_EXPECTED_H_INCLUDED
|
|
|
|
#include <xrpl/basics/contract.h>
|
|
|
|
#include <boost/outcome.hpp>
|
|
|
|
#include <stdexcept>
|
|
|
|
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 <class Impl>
|
|
static constexpr void
|
|
wide_value_check(Impl&& self)
|
|
{
|
|
if (!base::_has_value(std::forward<Impl>(self)))
|
|
Throw<bad_expected_access>();
|
|
}
|
|
|
|
template <class Impl>
|
|
static constexpr void
|
|
wide_error_check(Impl&& self)
|
|
{
|
|
if (!base::_has_error(std::forward<Impl>(self)))
|
|
Throw<bad_expected_access>();
|
|
}
|
|
|
|
template <class Impl>
|
|
static constexpr void
|
|
wide_exception_check(Impl&& self)
|
|
{
|
|
if (!base::_has_exception(std::forward<Impl>(self)))
|
|
Throw<bad_expected_access>();
|
|
}
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
// Definition of Unexpected, which is used to construct the unexpected
|
|
// return type of an Expected.
|
|
template <class E>
|
|
class Unexpected
|
|
{
|
|
public:
|
|
static_assert(!std::is_same<E, void>::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 <typename E, std::size_t N>
|
|
Unexpected(E (&)[N]) -> Unexpected<E const*>;
|
|
|
|
// Definition of Expected. All of the machinery comes from boost::result.
|
|
template <class T, class E>
|
|
class [[nodiscard]] Expected
|
|
: private boost::outcome_v2::result<T, E, detail::throw_policy>
|
|
{
|
|
using Base = boost::outcome_v2::result<T, E, detail::throw_policy>;
|
|
|
|
public:
|
|
template <typename U>
|
|
requires std::convertible_to<U, T>
|
|
constexpr Expected(U&& r)
|
|
: Base(boost::outcome_v2::in_place_type_t<T>{}, std::forward<U>(r))
|
|
{
|
|
}
|
|
|
|
template <typename U>
|
|
requires std::convertible_to<U, E> && (!std::is_reference_v<U>)
|
|
constexpr Expected(Unexpected<U> e)
|
|
: Base(boost::outcome_v2::in_place_type_t<E>{}, 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<void, E>. Allows returning either success
|
|
// (without a value) or the reason for the failure.
|
|
template <class E>
|
|
class [[nodiscard]] Expected<void, E>
|
|
: private boost::outcome_v2::result<void, E, detail::throw_policy>
|
|
{
|
|
using Base = boost::outcome_v2::result<void, E, detail::throw_policy>;
|
|
|
|
public:
|
|
// The default constructor makes a successful Expected<void, E>.
|
|
// This aligns with std::expected behavior proposed in P0323R10.
|
|
constexpr Expected() : Base(boost::outcome_v2::success())
|
|
{
|
|
}
|
|
|
|
template <typename U>
|
|
requires std::convertible_to<U, E> && (!std::is_reference_v<U>)
|
|
constexpr Expected(Unexpected<U> 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
|