diff --git a/include/libimp/expected.h b/include/libimp/expected.h index 714ea9c..9c25da3 100644 --- a/include/libimp/expected.h +++ b/include/libimp/expected.h @@ -138,19 +138,12 @@ template struct value_getter : data_union { using data_union::data_union; - value_getter(S const &other) : data_union(nullptr) { - if (bool(static_cast(other))) { - construct>(this, in_place, other.value()); + template + value_getter(U &&other) : data_union(nullptr) { + if (other) { + construct>(this, in_place, std::forward(other).value()); } else { - construct>(this, unexpected, other.error()); - } - } - - value_getter(S &&other) : data_union(nullptr) { - if (bool(static_cast(other))) { - construct>(this, in_place, std::move(other).value()); - } else { - construct>(this, unexpected, std::move(other).error()); + construct>(this, unexpected, std::forward(other).error()); } } @@ -198,19 +191,12 @@ template struct value_getter : data_union { using data_union::data_union; - value_getter(S const &other) : data_union(nullptr) { - if (bool(static_cast(other))) { + template + value_getter(U &&other) : data_union(nullptr) { + if (other) { construct>(this, in_place); } else { - construct>(this, unexpected, other.error()); - } - } - - value_getter(S &&other) : data_union(nullptr) { - if (bool(static_cast(other))) { - construct>(this, in_place); - } else { - construct>(this, unexpected, std::move(other).error()); + construct>(this, unexpected, std::forward(other).error()); } } @@ -235,6 +221,9 @@ struct value_getter : data_union { } }; +/** + * \brief Define the expected storage. + */ template struct storage : value_getter, T, E> { using getter_t = value_getter, T, E>; @@ -259,6 +248,16 @@ struct storage : value_getter, T, E> { : getter_t(std::move(other)) , has_value_(std::exchange(other.has_value_, false)) {} + template + storage(storage const &other) + : getter_t(other) + , has_value_(other.has_value_) {} + + template + storage(storage &&other) + : getter_t(std::move(other)) + , has_value_(std::exchange(other.has_value_, false)) {} + bool has_value() const noexcept { return has_value_; } @@ -315,4 +314,79 @@ R or_else(E &&exp, F &&f) { } // namespace detail_expected +/** + * \class template expected + * \brief Provides a way to store either of two values. + */ +template +class expected : public detail_expected::storage::type, E> { + public: + using value_type = typename std::remove_cv::type; + using error_type = E; + + using detail_expected::storage::storage; + + expected(expected const &) = default; + expected(expected &&) = default; + + expected() + : detail_expected::storage(in_place) {} + + expected &operator=(expected other) { + this->swap(other); + return *this; + } + + // Monadic operations + + template + auto and_then(F &&f) & { + return detail_expected::and_then(*this, std::forward(f)); + } + + template + auto and_then(F &&f) const & { + return detail_expected::and_then(*this, std::forward(f)); + } + + template + auto and_then(F &&f) && { + return detail_expected::and_then(std::move(*this), std::forward(f)); + } + + template + auto or_else(F &&f) & { + return detail_expected::or_else(*this, std::forward(f)); + } + + template + auto or_else(F &&f) const & { + return detail_expected::or_else(*this, std::forward(f)); + } + + template + auto or_else(F &&f) && { + return detail_expected::or_else(std::move(*this), std::forward(f)); + } +}; + +// Compares + +template +bool operator==(expected const &lhs, expected const &rhs) { + return (lhs.has_value() == rhs.has_value()) + && (lhs.has_value() ? *lhs == *rhs : lhs.error() == rhs.error()); +} + +template +bool operator==(expected const &lhs, expected const &rhs) { + return (lhs.has_value() == rhs.has_value()) + && (lhs.has_value() || lhs.error() == rhs.error()); +} + +template +bool operator!=(expected const &lhs, expected const &rhs) { + return !(lhs == rhs); +} + LIBIMP_NAMESPACE_END_