From 8ce397e8c0a83e49e390de9deb73d588e4931ecf Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Tue, 29 Oct 2024 21:02:25 -0500 Subject: Reworking of Result. --- include/fud_result.hpp | 214 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 186 insertions(+), 28 deletions(-) (limited to 'include/fud_result.hpp') 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 +#include // IWYU pragma: keep (placement new) +#include #include -#include namespace fud { +template +struct Okay { + T value; +}; + +template +struct Error { + E value; +}; + +using FudError = Error; + /** \brief A result type which contains either a T on success or an E on error. */ template class [[nodiscard]] Result { public: using ResultType = Result; - constexpr Result(const T& value) : m_value{value} + constexpr ~Result() noexcept { + destroy(); } - constexpr Result(const E& value) : m_value{value} + constexpr Result(const Okay& 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&& 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& 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& 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&& 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& 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&& 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 static constexpr ResultType okay(const Result& okayRes) { - return ResultType{okayRes.getOkay()}; + return ResultType::okay(okayRes.getOkay()); } template static constexpr ResultType okay(Result&& okayRes) { - return ResultType{okayRes.takeOkay()}; + return ResultType::okay(okayRes.takeOkay()); } template static constexpr ResultType error(const Result& errorRes) { - return ResultType{errorRes.getError()}; + return ResultType::error(errorRes.getError()); } template static constexpr ResultType error(Result&& 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(m_value); + fudAssert(isOkay()); + return *reinterpret_cast(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(m_value); + return *reinterpret_cast(m_data.data()); } [[nodiscard]] constexpr const E& getError() const& { - return std::get(m_value); + fudAssert(isError()); + return *reinterpret_cast(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(m_value); + return *reinterpret_cast(m_data.data()); } [[nodiscard]] constexpr T&& takeOkay() { - return std::move(std::get(m_value)); + fudAssert(isOkay()); + return std::move(*reinterpret_cast(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(m_value)); + return std::move(*reinterpret_cast(m_data.data())); } [[nodiscard]] constexpr E&& takeError() { - return std::move(std::get(m_value)); + fudAssert(isError()); + return std::move(*reinterpret_cast(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(m_value)); + return std::move(*reinterpret_cast(m_data.data())); } private: - constexpr Result() : m_value() + constexpr Result() + requires(std::is_default_constructible_v) + : 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 and not std::is_convertible_v) + : 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 and not std::is_convertible_v) + : m_data{}, m_discriminant{Discriminant::Okay} { + auto ptrValue = new (m_data.data()) T(std::move(value)); + fudAssert(ptrValue != nullptr); } - std::variant m_value; + constexpr Result(const E& value) + requires(not std::is_convertible_v and not std::is_convertible_v) + : 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 and not std::is_convertible_v) + : 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(m_data.data())->~T(); + m_discriminant = Discriminant::Invalid; + } else if (m_discriminant == Discriminant::Error) { + reinterpret_cast(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 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 -- cgit v1.2.3