summaryrefslogtreecommitdiff
path: root/include/fud_result.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/fud_result.hpp')
-rw-r--r--include/fud_result.hpp214
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