From 6ece7437e8ff80751ba39ee6f4693c8f589b2860 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Mon, 30 Oct 2017 10:43:43 +0000 Subject: [PATCH] More comparisons --- expected.hpp | 721 ++++++++++++++++++++++++++------------------------- 1 file changed, 367 insertions(+), 354 deletions(-) diff --git a/expected.hpp b/expected.hpp index 338604b..e09698c 100644 --- a/expected.hpp +++ b/expected.hpp @@ -16,9 +16,9 @@ #include #include +#include #include #include -#include #if (defined(_MSC_VER) && _MSC_VER == 1900) #define TL_EXPECTED_MSVC2015 @@ -163,9 +163,8 @@ constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { return lhs.value() >= rhs.value(); } -template -unexpected make_unexpected (E&& e) { - return unexpected(std::forward(e)); +template unexpected make_unexpected(E &&e) { + return unexpected(std::forward(e)); } struct unexpect_t { @@ -398,51 +397,52 @@ template struct expected_storage_base { }; // TODO, conditionally delete things - template struct expected_ctor_base {}; - template ::value && std::is_copy_assignable::value && - std::is_copy_constructible::value && std::is_copy_constructible::value && - std::is_nothrow_move_constructible::value), - bool = (std::is_move_constructible::value && std::is_move_assignable::value && - std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value)> - struct expected_assign_base { - expected_assign_base() = default; - ~expected_assign_base() = default; - expected_assign_base(const expected_assign_base&) = default; - expected_assign_base(expected_assign_base&&) noexcept = default; - expected_assign_base& operator=(const expected_assign_base&) = default; - expected_assign_base& operator=(expected_assign_base&&) noexcept = default; - }; +template struct expected_ctor_base {}; +template ::value && + std::is_copy_assignable::value && + std::is_copy_constructible::value && + std::is_copy_constructible::value && + std::is_nothrow_move_constructible::value), + bool = (std::is_move_constructible::value && + std::is_move_assignable::value && + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value)> +struct expected_assign_base { + expected_assign_base() = default; + ~expected_assign_base() = default; + expected_assign_base(const expected_assign_base &) = default; + expected_assign_base(expected_assign_base &&) noexcept = default; + expected_assign_base &operator=(const expected_assign_base &) = default; + expected_assign_base &operator=(expected_assign_base &&) noexcept = default; +}; - template - struct expected_assign_base { - expected_assign_base() = default; - ~expected_assign_base() = default; - expected_assign_base(const expected_assign_base&) = default; - expected_assign_base(expected_assign_base&&) noexcept = default; - expected_assign_base& operator=(const expected_assign_base&) = default; - expected_assign_base& operator=(expected_assign_base&&) noexcept = delete; - }; +template struct expected_assign_base { + expected_assign_base() = default; + ~expected_assign_base() = default; + expected_assign_base(const expected_assign_base &) = default; + expected_assign_base(expected_assign_base &&) noexcept = default; + expected_assign_base &operator=(const expected_assign_base &) = default; + expected_assign_base &operator=(expected_assign_base &&) noexcept = delete; +}; - template - struct expected_assign_base { - expected_assign_base() = default; - ~expected_assign_base() = default; - expected_assign_base(const expected_assign_base&) = default; - expected_assign_base(expected_assign_base&&) noexcept = default; - expected_assign_base& operator=(const expected_assign_base&) = delete; - expected_assign_base& operator=(expected_assign_base&&) noexcept = default; - }; +template struct expected_assign_base { + expected_assign_base() = default; + ~expected_assign_base() = default; + expected_assign_base(const expected_assign_base &) = default; + expected_assign_base(expected_assign_base &&) noexcept = default; + expected_assign_base &operator=(const expected_assign_base &) = delete; + expected_assign_base &operator=(expected_assign_base &&) noexcept = default; +}; - template - struct expected_assign_base { - expected_assign_base() = default; - ~expected_assign_base() = default; - expected_assign_base(const expected_assign_base&) = default; - expected_assign_base(expected_assign_base&&) noexcept = default; - expected_assign_base& operator=(const expected_assign_base&) = delete; - expected_assign_base& operator=(expected_assign_base&&) noexcept = delete; - }; +template struct expected_assign_base { + expected_assign_base() = default; + ~expected_assign_base() = default; + expected_assign_base(const expected_assign_base &) = default; + expected_assign_base(expected_assign_base &&) noexcept = default; + expected_assign_base &operator=(const expected_assign_base &) = delete; + expected_assign_base &operator=(expected_assign_base &&) noexcept = delete; +}; } // namespace detail template class bad_expected_access : public std::exception { @@ -464,8 +464,7 @@ private: template class expected : private detail::expected_storage_base, - private detail::expected_assign_base -{ + private detail::expected_assign_base { static_assert(!std::is_reference::value, "T must not be a reference"); static_assert(!std::is_same>::value, "T must not be in_place_t"); @@ -490,7 +489,8 @@ public: typedef E error_type; typedef unexpected unexpected_type; -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) /// \group and_then /// Carries out some operation which returns an optional on the stored object /// if there is one. \requires `std::invoke(std::forward(f), value())` @@ -604,7 +604,8 @@ public: #endif #endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) /// \brief Carries out some operation on the stored object if there is one. /// \returns Let `U` be the result of `std::invoke(std::forward(f), /// value())`. Returns a `std::expected`. The return value is empty if @@ -664,7 +665,8 @@ public: #endif #endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) /// \brief Carries out some operation on the stored object if there is one. /// \returns Let `U` be the result of `std::invoke(std::forward(f), /// value())`. Returns a `std::expected`. The return value is empty if @@ -753,7 +755,8 @@ public: detail::enable_if_t::value> * = nullptr, detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected const &e) : storage_base(unexpect, e.value()) {} + constexpr expected(unexpected const &e) + : storage_base(unexpect, e.value()) {} template < class G = E, @@ -853,163 +856,157 @@ public: constexpr expected(U &&v) : expected(in_place, std::forward(v)) {} // TODO conditionally delete - expected& operator=(const expected& rhs) { - return assign(rhs); + expected &operator=(const expected &rhs) { return assign(rhs); } + + expected &operator=(expected &&rhs) { return assign(std::move(rhs)); } + + template < + class U = T, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr, + detail::enable_if_t::value> * = + nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; } - expected& operator=(expected&& rhs) { - return assign(std::move(rhs)); + return *this; + } + + template < + class U = T, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr, + detail::enable_if_t::value> * = + nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + try { + ::new (valptr()) T(std::move(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } } - template , detail::decay_t>::value && - !detail::conjunction, std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)>* = nullptr, - detail::enable_if_t::value>* = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } - else { - err().~unexpected(); - ::new (valptr()) T (std::forward(v)); - this->m_has_val = true; - } + return *this; + } - return *this; + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + val().~T(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; } - template , detail::decay_t>::value && - !detail::conjunction, std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)>* = nullptr, - detail::enable_if_t::value>* = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } - else { - auto tmp = std::move(err()); - err().~unexpected(); - try { - ::new (valptr()) T (std::move(v)); - this->m_has_val = true; - } - catch (...) { - err() = std::move(tmp); - throw; - } - } + return *this; + } - return *this; + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + val().~T(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; } + return *this; + } - template ::value && std::is_assignable::value>* = nullptr> - expected &operator=(const unexpected &rhs) { - if (!has_value()) { - err() = rhs; - } - else { - val().~T(); - ::new (errptr()) unexpected (rhs); - this->m_has_val = false; - } - - return *this; + template ::value> * = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward(args)...); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; } + } - template ::value && std::is_move_assignable::value>* = nullptr> - expected &operator=(unexpected && rhs) noexcept { - if (!has_value()) { - err() = std::move(rhs); - } - else { - val().~T(); - ::new (errptr()) unexpected (std::move(rhs)); - this->m_has_val = false; - } + template ::value> * = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); - return *this; + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } } + } - template ::value>* = nullptr> - void emplace(Args &&... args) { - if (has_value()) { - val() = T(std::forward(args)...); - } - else { - err().~unexpected(); - ::new (valptr()) T (std::forward(args)...); - this->m_has_val = true; - } + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&... args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; } + } - template ::value>* = nullptr> - void emplace(Args &&... args) { - if (has_value()) { - val() = T(std::forward(args)...); - } - else { - auto tmp = std::move(err()); - err().~unexpected(); + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&... args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); - try { - ::new (valptr()) T (std::forward(args)...); - this->m_has_val = true; - } - catch (...) { - err() = std::move(tmp); - throw; - } - } - } - - template &, Args&&...>::value>* = nullptr> - void emplace(std::initializer_list il, Args &&... args) { - if (has_value()) { - T t (il, std::forward(args)...); - val() = std::move(t); - } - else { - err().~unexpected(); - ::new (valptr()) T (il, std::forward(args)...); - this->m_has_val = true; - } - } - - template &, Args&&...>::value>* = nullptr> - void emplace(std::initializer_list il, Args &&... args) { - if (has_value()) { - T t (il, std::forward(args)...); - val() = std::move(t); - } - else { - auto tmp = std::move(err()); - err().~unexpected(); - - try { - ::new (valptr()) T (il, std::forward(args)...); - this->m_has_val = true; - } - catch (...) { - err() = std::move(tmp); - throw; - } - } + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } } + } // TODO SFINAE void swap(expected &rhs) noexcept( @@ -1081,198 +1078,198 @@ public: return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); } - private: - template ::value>* = nullptr> - expected &assign(const expected &rhs) noexcept { - if (!has_value() && rhs.has_value()) { - err().~unexpected(); - ::new (valptr()) T (*rhs); - this->m_has_val = true; - return *this; - } - - return assign_common(rhs); + template ::value> + * = nullptr> + expected &assign(const expected &rhs) noexcept { + if (!has_value() && rhs.has_value()) { + err().~unexpected(); + ::new (valptr()) T(*rhs); + this->m_has_val = true; + return *this; } - template ::value && std::is_nothrow_move_constructible::value>* = nullptr> - expected &assign(const expected &rhs) noexcept { - if (!has_value() && rhs.has_value()) { - T tmp = *rhs; - err().~unexpected(); - ::new (valptr()) T (std::move(tmp)); - this->m_has_val = true; - return *this; - } + return assign_common(rhs); + } - return assign_common(rhs); + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + expected &assign(const expected &rhs) noexcept { + if (!has_value() && rhs.has_value()) { + T tmp = *rhs; + err().~unexpected(); + ::new (valptr()) T(std::move(tmp)); + this->m_has_val = true; + return *this; } - template ::value && !std::is_nothrow_move_constructible::value>* = nullptr> - expected &assign(const expected &rhs) { - if (!has_value() && rhs.has_value()) { - auto tmp = std::move(err()); - err().~unexpected(); + return assign_common(rhs); + } - try { - ::new (valptr()) T (*rhs); - this->m_has_val = true; - } - catch(...) { - err() = std::move(tmp); - throw; - } - this->m_has_val = true; - return *this; - } + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + expected &assign(const expected &rhs) { + if (!has_value() && rhs.has_value()) { + auto tmp = std::move(err()); + err().~unexpected(); - return assign_common(rhs); + try { + ::new (valptr()) T(*rhs); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } + this->m_has_val = true; + return *this; } - template ::value>* = nullptr> - expected &assign(expected && rhs) noexcept { - if (!has_value() && rhs.has_value()) { - err().~unexpected(); - ::new (valptr()) T (*std::move(rhs)); - this->m_has_val = true; - return *this; - } + return assign_common(rhs); + } - return assign_common(rhs); - } + template ::value> + * = nullptr> + expected &assign(expected &&rhs) noexcept { + if (!has_value() && rhs.has_value()) { + err().~unexpected(); + ::new (valptr()) T(*std::move(rhs)); + this->m_has_val = true; + return *this; + } - template ::value>* = nullptr> - expected &assign(expected && rhs) { - if (!has_value() && rhs.has_value()) { - auto tmp = std::move(err()); - err().~unexpected(); - try { - ::new (valptr()) T (*std::move(rhs)); - this->m_has_val = true; - } - catch (...) { - err() = std::move(tmp); - throw; - } + return assign_common(rhs); + } - return *this; - } + template ::value> + * = nullptr> + expected &assign(expected &&rhs) { + if (!has_value() && rhs.has_value()) { + auto tmp = std::move(err()); + err().~unexpected(); + try { + ::new (valptr()) T(*std::move(rhs)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } - return assign_common(rhs); - } + return *this; + } + return assign_common(rhs); + } - template - expected& assign_common(Rhs&& rhs) { - if (has_value()) { - if (rhs.has_value()) { - val() = *std::forward(rhs); - } - else { - val().~T(); - ::new (errptr()) unexpected (std::forward(rhs).err()); - } - } - else { - if (!rhs.has_value()) { - err() = std::forward(rhs).err(); - } - } + template expected &assign_common(Rhs &&rhs) { + if (has_value()) { + if (rhs.has_value()) { + val() = *std::forward(rhs); + } else { + val().~T(); + ::new (errptr()) unexpected(std::forward(rhs).err()); + } + } else { + if (!rhs.has_value()) { + err() = std::forward(rhs).err(); + } + } - return *this; - } + return *this; + } }; - namespace detail { - template using err_t = typename detail::decay_t::error_type; - template using ret_t = expected>; +namespace detail { +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; #ifdef TL_EXPECTED_CXX14 - template (), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - constexpr auto map_impl(Exp &&exp, F &&f) { - using result = ret_t; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); +template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_impl(Exp &&exp, F &&f) { + using result = ret_t; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(monostate{}); } - template (), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - auto map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return result(monostate{}); - } - - return result(unexpect, std::forward(exp).error()); - } + return result(unexpect, std::forward(exp).error()); +} #else - template (), - *std::declval())), - detail::enable_if_t::value> * = nullptr> +template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> - constexpr auto map_impl(Exp &&exp, F &&f) -> ret_t { - using result = ret_t; +constexpr auto map_impl(Exp &&exp, F &&f) -> ret_t { + using result = ret_t; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return tl::monostate{}; } - template (), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - - auto map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return tl::monostate{}; - } - - return unexpected>(std::forward(exp).error()); - } + return unexpected>(std::forward(exp).error()); +} #endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) - template (), - *std::declval()))> - constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = ret_t; - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, - detail::invoke(std::forward(f), - std::forward(exp).error())); - } +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) +template (), + *std::declval()))> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = ret_t; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} #else - template (), - *std::declval()))> - constexpr auto map_error_impl(Exp &&exp, F &&f) -> ret_t { - using result = ret_t; +template (), + *std::declval()))> +constexpr auto map_error_impl(Exp &&exp, F &&f) -> ret_t { + using result = ret_t; - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, - detail::invoke(std::forward(f), - std::forward(exp).error())); - } + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} #endif - - } - -// TODO -template class expected {}; +} // namespace detail template constexpr bool operator==(const expected &lhs, @@ -1288,14 +1285,32 @@ constexpr bool operator!=(const expected &lhs, ? true : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); } - template +template constexpr bool operator<(const expected &lhs, const expected &rhs) { return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); + ? (!lhs.has_value() ? true : false) + : (!lhs.has_value() ? lhs.error() < rhs.error() : *lhs < *rhs); +} +template +constexpr bool operator>(const expected &lhs, const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? (!lhs.has_value() ? false : true) + : (!lhs.has_value() ? lhs.error() > rhs.error() : *lhs > *rhs); +} +template +constexpr bool operator<=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? (!lhs.has_value() ? false : true) + : (!lhs.has_value() ? lhs.error() <= rhs.error() : *lhs <= *rhs); +} +template +constexpr bool operator>=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? (!lhs.has_value() ? true : false) + : (!lhs.has_value() ? lhs.error() >= rhs.error() : *lhs >= *rhs); } - -// TODO others template constexpr bool operator==(const expected &x, const U &v) { @@ -1346,8 +1361,6 @@ constexpr bool operator>=(const U &v, const expected &x) { return x.has_value() ? v >= *x : true; } -// TODO others - template constexpr bool operator==(const expected &x, const unexpected &e) { return x.has_value() ? true : x.error() == e.value();