From 22e745e2cb5949c6a529af076d1471ace79dff13 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 7 Mar 2018 12:37:59 +0100 Subject: First implementation of the Expected monad --- framework/src/errors.h | 246 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 framework/src/errors.h diff --git a/framework/src/errors.h b/framework/src/errors.h new file mode 100644 index 00000000..f6c6cfab --- /dev/null +++ b/framework/src/errors.h @@ -0,0 +1,246 @@ +#pragma once + +#include + +// A somewhat implementation of the expected monad, proposed here: +// https://isocpp.org/files/papers/n4015.pdf + +// A class used to differentiate errors and values when they are of the same type. +template +class Unexpected +{ + + static_assert(!std::is_same::value, "Cannot have an Unexpected void"); + +public: + Unexpected() = delete; + + constexpr explicit Unexpected(const Error &error) : mValue(error) {} + constexpr explicit Unexpected(Error &&error) : mValue(std::move(error)) {} + + // For implicit conversions when doing makeUnexpected(other) + template + constexpr explicit Unexpected(const Unexpected &error) : mValue(error.value()) {} + template + constexpr explicit Unexpected(Unexpected &&error) : mValue(std::move(error.value())) {} + + constexpr const Error &value() const & + { + return mValue; + } + Error &value() & + { + return mValue; + } + + constexpr const Error &&value() const && + { + return std::move(mValue); + } + Error &&value() && + { + return std::move(mValue); + } + +private: + Error mValue; +}; + +template +Unexpected::type> makeUnexpected(Error &&e) +{ + return Unexpected::type>(std::forward(e)); +} + +template +bool operator==(const Unexpected &lhs, const Unexpected &rhs) +{ + return lhs.value() == rhs.value(); +} + +template +bool operator!=(const Unexpected &lhs, const Unexpected &rhs) +{ + return lhs.value() != rhs.value(); +} + +namespace detail { + +namespace tags { +struct Expected +{}; +struct Unexpected +{}; +} // namespace tags + +// Write functions here when storage related and when Type != void +template +struct StorageBase +{ +protected: + // Rule of 5 {{{ + + constexpr void copyPartFromOther(const StorageBase &other) + { + if (isValue) { + mValue = other.mValue; + } else { + mError = other.mError; + } + } + + void movePartFromOther(StorageBase &&other) + { + if (isValue) { + mValue = std::move(other.mValue); + } else { + mError = std::move(other.mError); + } + } + + StorageBase(const StorageBase &other) : isValue(other.isValue) + { + copyPartFromOther(other); + } + + StorageBase(StorageBase &&other) : isValue(other.isValue) + { + movePartFromOther(std::move(other)); + } + + constexpr StorageBase &operator=(const StorageBase &other) + { + isValue = other.isValue; + copyPartFromOther(other); + return *this; + } + + constexpr StorageBase &operator=(StorageBase &&other) + { + isValue = other.isValue; + movePartFromOther(other); + return *this; + } + + ~StorageBase() + { + if (isValue) { + mValue.~Type(); + } else { + mError.~Unexpected(); + } + } + + // }}} + + template + constexpr StorageBase(tags::Expected, Args &&... args) + : mValue(std::forward(args)...), isValue(true) + { + } + + template + constexpr StorageBase(tags::Unexpected, Args &&... args) + : mError(std::forward(args)...), isValue(false) + { + } + + union + { + Unexpected mError; + Type mValue; + }; + bool isValue; +}; + +// Write functions here when storage related and when Type == void +template +struct StorageBase +{ +protected: + constexpr StorageBase(tags::Expected) : isValue(true) {} + + template + constexpr StorageBase(tags::Unexpected, Args &&... args) + : mError(std::forward(args)...), isValue(false) + { + } + + Unexpected mError; + bool isValue; +}; + +// Write functions here when storage related, whether Type is void or not +template +struct Storage : StorageBase +{ +protected: + // Forward the construction to StorageBase + using StorageBase::StorageBase; +}; + +// Write functions here when dev API related and when Type != void +template +struct ExpectedBase : detail::Storage +{ + constexpr ExpectedBase() : detail::Storage(detail::tags::Expected{}) {} + + template + constexpr ExpectedBase(const Unexpected &error) + : detail::Storage(detail::tags::Unexpected{}, error) + { + } + template + constexpr ExpectedBase(Unexpected &&error) + : detail::Storage(detail::tags::Unexpected{}, std::move(error)) + { + } + + constexpr ExpectedBase(const Type &value) + : detail::Storage(detail::tags::Expected{}, value) + { + } + constexpr ExpectedBase(Type &&value) + : detail::Storage(detail::tags::Expected{}, std::move(value)) + { + } + + constexpr const Type &value() const & + { + return this->mValue; + } +}; + +// Write functions here when dev API related and when Type == void +template +struct ExpectedBase : detail::Storage +{ + // Rewrite constructors for unexpected because Expected doesn't have direct access to it. + template + constexpr ExpectedBase(const Unexpected &error) + : detail::Storage(detail::tags::Unexpected{}, error) + { + } + template + constexpr ExpectedBase(Unexpected &&error) + : detail::Storage(detail::tags::Unexpected{}, std::move(error)) + { + } +}; + +} // namespace detail + +// Write functions here when dev API related, whether Type is void or not +template +class Expected : public detail::ExpectedBase +{ + static_assert(!std::is_same::value, "Expected with void Error is not implemented"); + +public: + using detail::ExpectedBase::ExpectedBase; + + constexpr const Error &error() const & + { + return this->mError.value(); + } +}; -- cgit v1.2.3