diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-10-29 21:02:25 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-10-29 21:02:25 -0500 |
commit | 8ce397e8c0a83e49e390de9deb73d588e4931ecf (patch) | |
tree | 31f4f4facf0cb75535aaec130d606c54fe97b2d8 /include/fud_result.hpp | |
parent | f281050ddb3b9d658cff67a254eedc3b79de5c5d (diff) |
Reworking of Result.
Diffstat (limited to 'include/fud_result.hpp')
-rw-r--r-- | include/fud_result.hpp | 214 |
1 files changed, 186 insertions, 28 deletions
diff --git a/include/fud_result.hpp b/include/fud_result.hpp index 497b007..b91a31a 100644 --- a/include/fud_result.hpp +++ b/include/fud_result.hpp @@ -18,31 +18,127 @@ #ifndef FUD_RESULT_HPP #define FUD_RESULT_HPP +#include "fud_assert.hpp" +#include "fud_option.hpp" +#include "fud_status.hpp" + +#include <cstdint> +#include <new> // IWYU pragma: keep (placement new) +#include <type_traits> #include <utility> -#include <variant> namespace fud { +template <typename T> +struct Okay { + T value; +}; + +template <typename E> +struct Error { + E value; +}; + +using FudError = Error<FudStatus>; + /** \brief A result type which contains either a T on success or an E on error. */ template <typename T, typename E> class [[nodiscard]] Result { public: using ResultType = Result<T, E>; - constexpr Result(const T& value) : m_value{value} + constexpr ~Result() noexcept { + destroy(); } - constexpr Result(const E& value) : m_value{value} + constexpr Result(const Okay<T>& value) : m_data{}, m_discriminant{Discriminant::Okay} { + auto ptrValue = new (m_data.data()) T(value.value); + fudAssert(ptrValue != nullptr); } - constexpr Result(T&& value) : m_value{std::move(value)} + constexpr Result(Okay<T>&& value) : m_data{}, m_discriminant{Discriminant::Okay} { + auto ptrValue = new (m_data.data()) T(std::move(value.value)); + fudAssert(ptrValue != nullptr); } - constexpr Result(E&& value) : m_value{std::move(value)} + constexpr Result(const Error<E>& value) : m_data{}, m_discriminant{Discriminant::Error} { + auto ptrValue = new (m_data.data()) E(value.value); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(E&& value) : m_data{}, m_discriminant{Discriminant::Error} + { + auto ptrValue = new (m_data.data()) E(std::move(value.value)); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(const Result<T, E>& rhs) : m_data{}, m_discriminant{rhs.m_discriminant} + { + fudAssert(m_discriminant != Discriminant::Invalid); + if (isOkay()) { + auto ptrValue = new (m_data.data()) T(rhs.getOkay()); + fudAssert(ptrValue != nullptr); + } else { + auto ptrValue = new (m_data.data()) E(rhs.getError()); + fudAssert(ptrValue != nullptr); + } + } + + constexpr Result(Result<T, E>&& rhs) : m_data{}, m_discriminant{rhs.m_discriminant} + { + fudAssert(m_discriminant != Discriminant::Invalid); + if (isOkay()) { + auto ptrValue = new (m_data.data()) T(rhs.takeOkay()); + fudAssert(ptrValue != nullptr); + } else { + auto ptrValue = new (m_data.data()) E(rhs.takeError()); + fudAssert(ptrValue != nullptr); + } + rhs.m_discriminant = Discriminant::Invalid; + } + + constexpr Result& operator=(const Result<T, E>& rhs) + { + if (&rhs == this) { + return *this; + } + destroy(); + + m_discriminant = rhs.m_discriminant; + fudAssert(m_discriminant != Discriminant::Invalid); + if (isOkay()) { + auto ptrValue = new (m_data.data()) T(rhs.getOkay()); + fudAssert(ptrValue != nullptr); + } else { + auto ptrValue = new (m_data.data()) E(rhs.getError()); + fudAssert(ptrValue != nullptr); + } + + return *this; + } + + constexpr Result& operator=(Result<T, E>&& rhs) + { + if (&rhs == this) { + return *this; + } + destroy(); + + m_discriminant = rhs.m_discriminant; + fudAssert(m_discriminant != Discriminant::Invalid); + if (isOkay()) { + auto ptrValue = new (m_data.data()) T(rhs.takeOkay()); + fudAssert(ptrValue != nullptr); + } else { + auto ptrValue = new (m_data.data()) E(rhs.takeError()); + fudAssert(ptrValue != nullptr); + } + + return *this; } static constexpr ResultType okay(const T& okay) @@ -68,40 +164,43 @@ class [[nodiscard]] Result { template <typename F> static constexpr ResultType okay(const Result<T, F>& okayRes) { - return ResultType{okayRes.getOkay()}; + return ResultType::okay(okayRes.getOkay()); } template <typename F> static constexpr ResultType okay(Result<T, F>&& okayRes) { - return ResultType{okayRes.takeOkay()}; + return ResultType::okay(okayRes.takeOkay()); } template <typename U> static constexpr ResultType error(const Result<U, E>& errorRes) { - return ResultType{errorRes.getError()}; + return ResultType::error(errorRes.getError()); } template <typename U> static constexpr ResultType error(Result<U, E>&& errorRes) { - return ResultType{errorRes.takeError()}; + return ResultType::error(errorRes.takeError()); } [[nodiscard]] constexpr bool isOkay() const { - return (m_value.index() == 0); + fudAssert(m_discriminant != Discriminant::Invalid); + return (m_discriminant == Discriminant::Okay); } [[nodiscard]] constexpr bool isError() const { - return (m_value.index() == 1); + fudAssert(m_discriminant != Discriminant::Invalid); + return (m_discriminant == Discriminant::Error); } [[nodiscard]] constexpr const T& getOkay() const& { - return std::get<T>(m_value); + fudAssert(isOkay()); + return *reinterpret_cast<const T*>(m_data.data()); } [[nodiscard]] constexpr const T& getOkayOr(const T& alternative) const& @@ -109,12 +208,13 @@ class [[nodiscard]] Result { if (!isOkay()) { return alternative; } - return std::get<T>(m_value); + return *reinterpret_cast<const T*>(m_data.data()); } [[nodiscard]] constexpr const E& getError() const& { - return std::get<E>(m_value); + fudAssert(isError()); + return *reinterpret_cast<const E*>(m_data.data()); } [[nodiscard]] constexpr const E& getErrorOr(const E& alternative) const& @@ -122,12 +222,13 @@ class [[nodiscard]] Result { if (!isError()) { return alternative; } - return std::get<E>(m_value); + return *reinterpret_cast<const E*>(m_data.data()); } [[nodiscard]] constexpr T&& takeOkay() { - return std::move(std::get<T>(m_value)); + fudAssert(isOkay()); + return std::move(*reinterpret_cast<T*>(m_data.data())); } [[nodiscard]] constexpr T&& takeOkayOr(T&& alternative) @@ -135,12 +236,13 @@ class [[nodiscard]] Result { if (!isOkay()) { return std::move(alternative); } - return std::move(std::get<T>(m_value)); + return std::move(*reinterpret_cast<T*>(m_data.data())); } [[nodiscard]] constexpr E&& takeError() { - return std::move(std::get<E>(m_value)); + fudAssert(isError()); + return std::move(*reinterpret_cast<E*>(m_data.data())); } [[nodiscard]] constexpr E&& takeErrorOr(E&& alternative) @@ -148,24 +250,80 @@ class [[nodiscard]] Result { if (!isError()) { return std::move(alternative); } - return std::move(std::get<E>(m_value)); + return std::move(*reinterpret_cast<E*>(m_data.data())); } private: - constexpr Result() : m_value() + constexpr Result() + requires(std::is_default_constructible_v<T>) + : m_data{}, m_discriminant{Discriminant::Okay} + { + auto ptrValue = new (m_data.data()) T(); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(const T& value) + requires(not std::is_convertible_v<T, E> and not std::is_convertible_v<E, T>) + : m_data{}, m_discriminant{Discriminant::Okay} + { + auto ptrValue = new (m_data.data()) T(value); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(T&& value) + requires(not std::is_convertible_v<T, E> and not std::is_convertible_v<E, T>) + : m_data{}, m_discriminant{Discriminant::Okay} { + auto ptrValue = new (m_data.data()) T(std::move(value)); + fudAssert(ptrValue != nullptr); } - std::variant<T, E> m_value; + constexpr Result(const E& value) + requires(not std::is_convertible_v<T, E> and not std::is_convertible_v<E, T>) + : m_data{}, m_discriminant{Discriminant::Error} + { + auto ptrValue = new (m_data.data()) E(value); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(E&& value) + requires(not std::is_convertible_v<T, E> and not std::is_convertible_v<E, T>) + : m_data{}, m_discriminant{Discriminant::Error} + { + auto ptrValue = new (m_data.data()) E(std::move(value)); + fudAssert(ptrValue != nullptr); + } + + constexpr void destroy() noexcept + { + if (m_discriminant == Discriminant::Okay) { + reinterpret_cast<T*>(m_data.data())->~T(); + m_discriminant = Discriminant::Invalid; + } else if (m_discriminant == Discriminant::Error) { + reinterpret_cast<E*>(m_data.data())->~E(); + m_discriminant = Discriminant::Invalid; + } + } + + static constexpr auto Size = std::max(sizeof(T), sizeof(E)); + static constexpr auto Align = std::max(alignof(T), alignof(E)); + option_detail::DataArray<Size> m_data{}; + + enum class Discriminant : uint8_t + { + Invalid, + Okay, + Error, + } m_discriminant{Discriminant::Invalid}; }; -#define M_TakeOrReturn(HYGIENE_EXPRESSION) \ - ({ \ - auto HYGIENE_RESULT{(HYGIENE_EXPRESSION)}; \ - if (HYGIENE_RESULT.isError()) { \ - return HYGIENE_RESULT.takeError(); \ - } \ - HYGIENE_RESULT.takeOkay(); \ +#define M_TakeOrReturn(HYGIENE_RESULT_TYPE, HYGIENE_EXPRESSION) \ + ({ \ + auto HYGIENE_RESULT{(HYGIENE_EXPRESSION)}; \ + if (HYGIENE_RESULT.isError()) { \ + return HYGIENE_RESULT_TYPE::error(HYGIENE_RESULT.takeError()); \ + } \ + HYGIENE_RESULT.takeOkay(); \ }) } // namespace fud |