#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(); } };