From 81d7cba98ca0478dfba61ee248d4a7defe49bec6 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Tue, 19 Feb 2019 15:32:32 +0000 Subject: [PATCH] Tag dispatch swap --- tl/expected.hpp | 453 +++++++++++++++++++++++++----------------------- 1 file changed, 232 insertions(+), 221 deletions(-) diff --git a/tl/expected.hpp b/tl/expected.hpp index 9d8a0ee..359ef00 100644 --- a/tl/expected.hpp +++ b/tl/expected.hpp @@ -71,30 +71,31 @@ #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ std::is_trivially_destructible -// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector -// for non-copyable types -#elif (defined(__GNUC__) && __GNUC__ < 8 && \ - !defined(__clang__)) +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) #ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX #define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX namespace tl { namespace detail { -template -struct is_trivially_copy_constructible : std::is_trivially_copy_constructible{}; +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; #ifdef _GLIBCXX_VECTOR -template -struct is_trivially_copy_constructible> - : std::is_trivially_copy_constructible{}; +template +struct is_trivially_copy_constructible> + : std::is_trivially_copy_constructible {}; #endif - } -} +} // namespace detail +} // namespace tl #endif -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ tl::detail::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible #else /// \exclude #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ @@ -219,7 +220,7 @@ static constexpr unexpect_t unexpect{}; /// \exclude namespace detail { -template +template [[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED throw std::forward(e); @@ -235,7 +236,8 @@ template #ifndef TL_TRAITS_MUTEX #define TL_TRAITS_MUTEX // C++14-style aliases for brevity -template using remove_const_t = typename std::remove_const::type; +template +using remove_const_t = typename std::remove_const::type; template using remove_reference_t = typename std::remove_reference::type; template using decay_t = typename std::decay::type; @@ -275,9 +277,11 @@ template struct invoke_result_impl; template struct invoke_result_impl< - F, decltype(detail::invoke(std::declval(), std::declval()...), void()), + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), Us...> { - using type = decltype(detail::invoke(std::declval(), std::declval()...)); + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); }; template @@ -398,14 +402,10 @@ using is_move_constructible_or_void = is_void_or>; template -using is_copy_assignable_or_void = - is_void_or>; - +using is_copy_assignable_or_void = is_void_or>; template -using is_move_assignable_or_void = - is_void_or>; - +using is_move_assignable_or_void = is_void_or>; } // namespace detail @@ -837,9 +837,7 @@ struct expected_operations_base : expected_storage_base { } #endif - constexpr void destroy_val() { - get().~T(); - } + constexpr void destroy_val() { get().~T(); } }; // This base class provides some handy member functions which can be used in @@ -893,7 +891,7 @@ struct expected_operations_base : expected_storage_base { #endif constexpr void destroy_val() { - //no-op + // no-op } }; @@ -1225,7 +1223,9 @@ class expected : private detail::expected_move_assign_base, T *valptr() { return std::addressof(this->m_val); } const T *valptr() const { return std::addressof(this->m_val); } unexpected *errptr() { return std::addressof(this->m_unexpect); } - const unexpected *errptr() const { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } template ::value> * = nullptr> @@ -1381,8 +1381,8 @@ public: /// \group map /// \synopsis template constexpr auto map(F &&f) &&; template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) map(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } @@ -1494,8 +1494,8 @@ public: /// \requires `F` is invokable with `E`, and `std::invoke_result_t` /// must be void or convertible to `expcted`. /// \effects If `*this` has a value, returns `*this`. - /// Otherwise, if `f` returns `void`, calls `std::forward(f)(E)` and returns - /// `std::nullopt`. Otherwise, returns `std::forward(f)(E)`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)(E)` and + /// returns `std::nullopt`. Otherwise, returns `std::forward(f)(E)`. /// /// \group or_else template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { @@ -1837,194 +1837,204 @@ public: } } - template - detail::enable_if_t< - detail::is_swappable::value && detail::is_swappable::value && - std::is_nothrow_move_constructible::value && - !std::is_nothrow_move_constructible::value && !std::is_void::value> - swap(expected &rhs) noexcept( - detail::is_nothrow_swappable::value - &&std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value) { - if (has_value() && rhs.has_value()) { - using std::swap; - swap(val(), rhs.val()); - } else if (!has_value() && rhs.has_value()) { - rhs.swap(*this); - } else if (has_value()) { - auto temp = std::move(val()); - val().~T(); - try { - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(temp); - std::swap(this->m_has_val, rhs.m_has_val); - } catch (...) { - val() = std::move(temp); - throw; - } - } else { - using std::swap; - swap(err(), rhs.err()); - } +private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; + + void swap_where_both_have_value(expected &rhs, t_is_void) noexcept { + // swapping void is a no-op } - template - detail::enable_if_t< - detail::is_swappable::value && detail::is_swappable::value && - std::is_nothrow_move_constructible::value && !std::is_void::value> - swap(expected &rhs) noexcept(std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value && - detail::is_nothrow_swappable::value) { - if (has_value() && rhs.has_value()) { - using std::swap; - swap(val(), rhs.val()); - } else if (!has_value() && rhs.has_value()) { - rhs.swap(*this); - } else if (has_value()) { - auto temp = std::move(rhs.err()); + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept ( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, std::is_nothrow_move_constructible::type{}, + std::is_nothrow_move_constructible::type{}); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); rhs.err().~unexpected_type(); - try { - ::new (rhs.valptr()) T(val()); - val().~T(); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } catch (...) { - rhs.err() = std::move(temp); - throw; - } - } else { - using std::swap; - swap(err(), rhs.err()); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; } } - template + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + t_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); + try { + ::new (rhs.valptr()) T(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } + } + +public: + template detail::enable_if_t::value && detail::is_swappable::value && - std::is_void::value && - (std::is_move_constructible::value || - std::is_move_constructible::value)> + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> swap(expected &rhs) noexcept( - std::is_nothrow_move_constructible::value + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value &&detail::is_nothrow_swappable::value) { - if (has_value() && rhs.has_value()) { - // swapping void is a no-op - return; - } else if (!has_value() && rhs.has_value()) { - rhs.swap(*this); - } else if (has_value()) { - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - std::swap(this->m_has_val, rhs.m_has_val); - } else { - using std::swap; - swap(err(), rhs.err()); - } - } + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, std::is_void::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, std::is_void::type{}); + } else { + using std::swap; + swap(err(), rhs.err()); + } + } - /// \returns a pointer to the stored value - /// \requires a value is stored - /// \group pointer - constexpr const T *operator->() const { return valptr(); } - /// \group pointer - TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + constexpr const T *operator->() const { return valptr(); } + /// \group pointer + TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } - /// \returns the stored value - /// \requires a value is stored - /// \group deref - template ::value> * = nullptr> - constexpr const U &operator*() const & { - return val(); - } - /// \group deref - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &operator*() & { - return val(); - } - /// \group deref - template ::value> * = nullptr> - constexpr const U &&operator*() const && { - return std::move(val()); - } - /// \group deref - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&operator*() && { - return std::move(val()); - } + /// \returns the stored value + /// \requires a value is stored + /// \group deref + template ::value> * = nullptr> + constexpr const U &operator*() const & { + return val(); + } + /// \group deref + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + return val(); + } + /// \group deref + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + return std::move(val()); + } + /// \group deref + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + return std::move(val()); + } - /// \returns whether or not the optional has a value - /// \group has_value - constexpr bool has_value() const noexcept { return this->m_has_val; } - /// \group has_value - constexpr explicit operator bool() const noexcept { return this->m_has_val; } + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool has_value() const noexcept { return this->m_has_val; } + /// \group has_value + constexpr explicit operator bool() const noexcept { return this->m_has_val; } - /// \returns the contained value if there is one, otherwise throws - /// [bad_expected_access] - /// - /// \group value - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &value() const & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - /// \group value - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &value() & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - /// \group value - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &&value() const && { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return std::move(val()); - } - /// \group value - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&value() && { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return std::move(val()); - } + /// \returns the contained value if there is one, otherwise throws + /// [bad_expected_access] + /// + /// \group value + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + /// \group value + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + /// \group value + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return std::move(val()); + } + /// \group value + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return std::move(val()); + } - /// \returns the unexpected value - /// \requires there is an unexpected value - /// \group error - constexpr const E &error() const & { return err().value(); } - /// \group error - TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } - /// \group error - constexpr const E &&error() const && { return std::move(err().value()); } - /// \group error - TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } + /// \returns the unexpected value + /// \requires there is an unexpected value + /// \group error + constexpr const E &error() const & { return err().value(); } + /// \group error + TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } + /// \group error + constexpr const E &&error() const && { return std::move(err().value()); } + /// \group error + TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } - /// \returns the stored value if there is one, otherwise returns `u` - /// \group value_or - template constexpr T value_or(U &&v) const & { - static_assert(std::is_copy_constructible::value && - std::is_convertible::value, - "T must be copy-constructible and convertible to from U&&"); - return bool(*this) ? **this : static_cast(std::forward(v)); - } - /// \group value_or - template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { - static_assert(std::is_move_constructible::value && - std::is_convertible::value, - "T must be move-constructible and convertible to from U&&"); - return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); - } + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + template constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + /// \group value_or + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } }; /// \exclude @@ -2313,9 +2323,9 @@ template ::value> * = nullptr> constexpr auto or_else_impl(Exp &&exp, F &&f) { static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? std::forward(exp) - : detail::invoke(std::forward(f), std::forward(exp).error()); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); } template ().error())), detail::enable_if_t::value> * = nullptr> detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() - ? std::forward(exp) - : (detail::invoke(std::forward(f), std::forward(exp).error()), + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), std::forward(exp)); } #else @@ -2335,9 +2345,9 @@ template ::value> * = nullptr> auto or_else_impl(Exp &&exp, F &&f) -> Ret { static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? std::forward(exp) - : detail::invoke(std::forward(f), std::forward(exp).error()); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); } template ().error())), detail::enable_if_t::value> * = nullptr> detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() - ? std::forward(exp) - : (detail::invoke(std::forward(f), std::forward(exp).error()), - std::forward(exp)); + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); } #endif } // namespace detail @@ -2403,10 +2413,11 @@ constexpr bool operator!=(const unexpected &e, const expected &x) { } template ::value || std::is_move_constructible::value) && - detail::is_swappable::value && + detail::enable_if_t<(std::is_void::value || + std::is_move_constructible::value) && + detail::is_swappable::value && std::is_move_constructible::value && - detail::is_swappable::value> * = nullptr> + detail::is_swappable::value> * = nullptr> void swap(expected &lhs, expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs);