From beedb23d5c4e1b341d03c83a9cf587583eb43652 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Sat, 28 Oct 2017 14:16:50 +0100 Subject: [PATCH 1/6] Fix GCC --- expected.hpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/expected.hpp b/expected.hpp index f672b68..82396b2 100644 --- a/expected.hpp +++ b/expected.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #if (defined(_MSC_VER) && _MSC_VER == 1900) #define TL_EXPECTED_MSVC2015 @@ -432,7 +433,7 @@ public: typedef E error_type; typedef unexpected unexpected_type; -#ifdef TL_EXPECTED_CXX14 +#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())` @@ -544,9 +545,9 @@ public: : result(unexpect, std::move(this->error())); } #endif -#endif +#endif -#ifdef TL_EXPECTED_CXX14 +#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 @@ -606,7 +607,7 @@ public: #endif #endif -#ifdef TL_EXPECTED_CXX14 +#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 @@ -873,8 +874,9 @@ public: "T must be move-constructible and convertible to from U&&"); return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); } +}; -private: + namespace detail { template using err_t = typename detail::decay_t::error_type; template using ret_t = expected>; @@ -883,7 +885,7 @@ private: class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> - static constexpr auto map_impl(Exp &&exp, F &&f) { + 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))) @@ -894,7 +896,7 @@ private: class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> - static auto map_impl(Exp &&exp, F &&f) { + auto map_impl(Exp &&exp, F &&f) { using result = expected>; if (exp.has_value()) { detail::invoke(std::forward(f), *std::forward(exp)); @@ -909,7 +911,7 @@ private: *std::declval())), detail::enable_if_t::value> * = nullptr> - static constexpr auto map_impl(Exp &&exp, F &&f) -> 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), @@ -922,7 +924,7 @@ private: *std::declval())), detail::enable_if_t::value> * = nullptr> - static auto map_impl(Exp &&exp, F &&f) -> expected> { + auto map_impl(Exp &&exp, F &&f) -> expected> { if (exp.has_value()) { detail::invoke(std::forward(f), *std::forward(exp)); return tl::monostate{}; @@ -932,11 +934,11 @@ private: } #endif -#ifdef TL_EXPECTED_CXX14 +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) template (), *std::declval()))> - static constexpr auto map_error_impl(Exp &&exp, F &&f) { + constexpr auto map_error_impl(Exp &&exp, F &&f) { using result = ret_t; return exp.has_value() ? result(*std::forward(exp)) @@ -948,15 +950,17 @@ private: template (), *std::declval()))> - static constexpr auto map_error_impl(Exp &&exp, F &&f) -> ret_t { + constexpr auto map_error_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(*std::forward(exp)) + : result(unexpect, + detail::invoke(std::forward(f), + std::forward(exp).error())); } #endif -}; + } // TODO template class expected {}; From 8b800d6d0e166741fbe046e524af9d1686b62e17 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Sat, 28 Oct 2017 14:17:05 +0100 Subject: [PATCH 2/6] License --- COPYING | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/COPYING @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. From ec3c96af64e667e8479d31de98bfe287153c746c Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Sun, 29 Oct 2017 20:01:53 +0000 Subject: [PATCH 3/6] Untested assignment and emplace --- expected.hpp | 263 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 254 insertions(+), 9 deletions(-) diff --git a/expected.hpp b/expected.hpp index 82396b2..5b14d0e 100644 --- a/expected.hpp +++ b/expected.hpp @@ -58,6 +58,13 @@ template using enable_if_t = typename std::enable_if::type; template using decay_t = typename std::decay::type; +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + // Trait for checking if a type is a tl::expected template struct is_expected_impl : std::false_type {}; template @@ -795,15 +802,229 @@ public: std::is_convertible::value> * = nullptr> constexpr expected(U &&v) : expected(in_place, std::forward(v)) {} - // TODO - expected &operator=(const expected &); - expected &operator=(expected &&) noexcept; - template expected &operator=(U &&); - template expected &operator=(const unexpected &); - template expected &operator=(unexpected &&) noexcept; - template void emplace(Args &&...); - template - void emplace(std::initializer_list, Args &&...); + // TODO conditionally delete + template ::value>* = nullptr> + expected &operator=(const expected &rhs) noexcept { + if (!has_value() && rhs.has_value()) { + err().~unexpected(); + ::new (valptr()) T (*rhs); + this->m_has_value = true; + return *this; + } + + return assign(rhs); + } + + template ::value && std::is_nothrow_move_constructible::value>* = nullptr> + expected &operator=(const expected &rhs) noexcept { + if (!has_value() && rhs.has_value()) { + T tmp = *rhs; + err().~unexpected(); + ::new (valptr()) T (std::move(tmp)); + this->m_has_value = true; + return *this; + } + + return assign(rhs); + } + + template ::value && !std::is_nothrow_move_constructible::value>* = nullptr> + expected &operator=(const expected &rhs) { + if (!has_value() && rhs.has_value()) { + auto tmp = std::move(err()); + err().~unexpected(); + + try { + ::new (valptr()) T (*rhs); + this->m_has_value = true; + } + catch(...) { + err() = std::move(tmp); + throw; + } + this->m_has_value = true; + return *this; + } + + return assign(rhs); + } + + template ::value>* = nullptr> + expected &operator=(expected && rhs) noexcept { + if (!has_value() && rhs.has_value()) { + err().~unexpected(); + ::new (valptr()) T (*std::move(rhs)); + } + + return assign(rhs); + } + + template ::value>* = nullptr> + expected &operator=(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_value = true; + } + catch (...) { + err() = std::move(tmp); + throw; + } + } + + return assign(rhs); + } + + 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_value = true; + } + + return *this; + } + + 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_value = true; + } + catch (...) { + err() = std::move(tmp); + throw; + } + } + + 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 && 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>* = 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_value = true; + } + } + + template ::value>* = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward(args)...); + } + else { + auto tmp = std::move(err()); + err().~unexpected(); + + try { + ::new (valptr()) T (std::forward(args)...); + this->m_has_value = 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_value = 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_value = true; + } + catch (...) { + err() = std::move(tmp); + throw; + } + } + } // TODO SFINAE void swap(expected &rhs) noexcept( @@ -874,6 +1095,28 @@ public: "T must be move-constructible and convertible to from U&&"); return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); } + + +private: + template + expected& assign(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; + } }; namespace detail { @@ -960,6 +1203,8 @@ public: std::forward(exp).error())); } #endif + + } // TODO From d92ed4b0cbfca832d2b783262d4265ca866cc128 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Sun, 29 Oct 2017 20:25:40 +0000 Subject: [PATCH 4/6] Call function --- expected.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/expected.hpp b/expected.hpp index 5b14d0e..5108ee7 100644 --- a/expected.hpp +++ b/expected.hpp @@ -42,7 +42,7 @@ #if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ defined(TL_EXPECTED_GCC49)) && \ - !defined(TL_EXPECTED_GCC50) + !defined(TL_EXPECTED_GCC54) /// \exclude #define TL_EXPECTED_11_CONSTEXPR #else @@ -995,7 +995,7 @@ public: void emplace(std::initializer_list il, Args &&... args) { if (has_value()) { T t (il, std::forward(args)...); - *val = std::move(t); + val() = std::move(t); } else { err().~unexpected(); @@ -1009,7 +1009,7 @@ public: void emplace(std::initializer_list il, Args &&... args) { if (has_value()) { T t (il, std::forward(args)...); - *val = std::move(t); + val() = std::move(t); } else { auto tmp = std::move(err()); From de01c7bb4e6de62b6d7b68e4f92c15002527839f Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Mon, 30 Oct 2017 07:13:07 +0000 Subject: [PATCH 5/6] Fix assignment --- expected.hpp | 184 ++++++++++++++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 84 deletions(-) diff --git a/expected.hpp b/expected.hpp index 5108ee7..41ab1a7 100644 --- a/expected.hpp +++ b/expected.hpp @@ -163,6 +163,11 @@ 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)); +} + struct unexpect_t { unexpect_t() = default; }; @@ -393,7 +398,7 @@ template struct expected_storage_base { }; // TODO, conditionally delete things -template class expected_ctor_base {}; + template struct expected_ctor_base {}; } // namespace detail template class bad_expected_access : public std::exception { @@ -414,8 +419,7 @@ private: }; template -class expected : private detail::expected_storage_base, - private detail::expected_ctor_base { +class expected : private detail::expected_storage_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"); @@ -696,14 +700,14 @@ public: detail::enable_if_t::value> * = nullptr> explicit constexpr expected(unexpected const &e) - : storage_base(unexpect, e) {} + : storage_base(unexpect, e.value()) {} template < class G = E, detail::enable_if_t::value> * = nullptr, detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected const &e) : storage_base(unexpect, e) {} + constexpr expected(unexpected const &e) : storage_base(unexpect, e.value()) {} template < class G = E, @@ -711,7 +715,7 @@ public: detail::enable_if_t::value> * = nullptr> explicit constexpr expected(unexpected &&e) noexcept( std::is_nothrow_constructible::value) - : storage_base(unexpect, std::move(e)) {} + : storage_base(unexpect, std::move(e.value())) {} template < class G = E, @@ -719,7 +723,7 @@ public: detail::enable_if_t::value> * = nullptr> constexpr expected(unexpected &&e) noexcept( std::is_nothrow_constructible::value) - : storage_base(unexpect, std::move(e)) {} + : storage_base(unexpect, std::move(e.value())) {} template ::value> * = @@ -803,80 +807,13 @@ public: constexpr expected(U &&v) : expected(in_place, std::forward(v)) {} // TODO conditionally delete - template ::value>* = nullptr> - expected &operator=(const expected &rhs) noexcept { - if (!has_value() && rhs.has_value()) { - err().~unexpected(); - ::new (valptr()) T (*rhs); - this->m_has_value = true; - return *this; - } - + expected& operator=(const expected& rhs) { return assign(rhs); } - template ::value && std::is_nothrow_move_constructible::value>* = nullptr> - expected &operator=(const expected &rhs) noexcept { - if (!has_value() && rhs.has_value()) { - T tmp = *rhs; - err().~unexpected(); - ::new (valptr()) T (std::move(tmp)); - this->m_has_value = true; - return *this; - } - - return assign(rhs); + expected& operator=(expected&& rhs) { + return assign(std::move(rhs)); } - - template ::value && !std::is_nothrow_move_constructible::value>* = nullptr> - expected &operator=(const expected &rhs) { - if (!has_value() && rhs.has_value()) { - auto tmp = std::move(err()); - err().~unexpected(); - - try { - ::new (valptr()) T (*rhs); - this->m_has_value = true; - } - catch(...) { - err() = std::move(tmp); - throw; - } - this->m_has_value = true; - return *this; - } - - return assign(rhs); - } - - template ::value>* = nullptr> - expected &operator=(expected && rhs) noexcept { - if (!has_value() && rhs.has_value()) { - err().~unexpected(); - ::new (valptr()) T (*std::move(rhs)); - } - - return assign(rhs); - } - - template ::value>* = nullptr> - expected &operator=(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_value = true; - } - catch (...) { - err() = std::move(tmp); - throw; - } - } - - return assign(rhs); - } - template , detail::decay_t>::value && @@ -891,7 +828,7 @@ public: else { err().~unexpected(); ::new (valptr()) T (std::forward(v)); - this->m_has_value = true; + this->m_has_val = true; } return *this; @@ -914,7 +851,7 @@ public: err().~unexpected(); try { ::new (valptr()) T (std::move(v)); - this->m_has_value = true; + this->m_has_val = true; } catch (...) { err() = std::move(tmp); @@ -965,7 +902,7 @@ public: else { err().~unexpected(); ::new (valptr()) T (std::forward(args)...); - this->m_has_value = true; + this->m_has_val = true; } } @@ -981,7 +918,7 @@ public: try { ::new (valptr()) T (std::forward(args)...); - this->m_has_value = true; + this->m_has_val = true; } catch (...) { err() = std::move(tmp); @@ -1000,7 +937,7 @@ public: else { err().~unexpected(); ::new (valptr()) T (il, std::forward(args)...); - this->m_has_value = true; + this->m_has_val = true; } } @@ -1017,7 +954,7 @@ public: try { ::new (valptr()) T (il, std::forward(args)...); - this->m_has_value = true; + this->m_has_val = true; } catch (...) { err() = std::move(tmp); @@ -1098,8 +1035,87 @@ public: 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 && 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); + } + + 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(); + + try { + ::new (valptr()) T (*rhs); + this->m_has_val = true; + } + catch(...) { + err() = std::move(tmp); + throw; + } + this->m_has_val = true; + return *this; + } + + 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; + } + + return assign_common(rhs); + } + + 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 *this; + } + + return assign_common(rhs); + } + + template - expected& assign(Rhs&& rhs) { + expected& assign_common(Rhs&& rhs) { if (has_value()) { if (rhs.has_value()) { val() = *std::forward(rhs); From 2c90606f5935d6efc329356359423b3369f16346 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Mon, 30 Oct 2017 07:46:41 +0000 Subject: [PATCH 6/6] Attempt to conditionally delete assignment --- CMakeLists.txt | 1 + expected.hpp | 56 +++++++++++++++++++++++++++++--- tests/assignment.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++ tests/emplace.cpp | 4 +++ 4 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 tests/assignment.cpp create mode 100644 tests/emplace.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d9ebc1..06ca9de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR}) # Make test executable set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/extensions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/assignment.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/constructors.cpp) add_executable(tests ${TEST_SOURCES}) diff --git a/expected.hpp b/expected.hpp index 41ab1a7..dba758e 100644 --- a/expected.hpp +++ b/expected.hpp @@ -399,6 +399,50 @@ 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_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 = delete; + }; } // namespace detail template class bad_expected_access : public std::exception { @@ -419,7 +463,9 @@ private: }; template -class expected : private detail::expected_storage_base { +class expected : private detail::expected_storage_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"); @@ -814,13 +860,15 @@ public: expected& operator=(expected&& rhs) { return assign(std::move(rhs)); } + 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> + std::is_nothrow_move_constructible::value)>* = nullptr, + detail::enable_if_t::value>* = nullptr> expected &operator=(U &&v) { if (has_value()) { val() = std::forward(v); @@ -864,7 +912,7 @@ public: template ::value && std::is_assignable::value>* = nullptr> + detail::enable_if_t::value && std::is_assignable::value>* = nullptr> expected &operator=(const unexpected &rhs) { if (!has_value()) { err() = rhs; @@ -879,7 +927,7 @@ public: } template ::value && std::is_move_assignable::value>* = nullptr> + detail::enable_if_t::value && std::is_move_assignable::value>* = nullptr> expected &operator=(unexpected && rhs) noexcept { if (!has_value()) { err() = std::move(rhs); diff --git a/tests/assignment.cpp b/tests/assignment.cpp new file mode 100644 index 0000000..ac0514f --- /dev/null +++ b/tests/assignment.cpp @@ -0,0 +1,76 @@ +#include "catch.hpp" +#include "expected.hpp" + +TEST_CASE("Simple assignment", "[assignment.simple]") { + tl::expected e1 = 42; + tl::expected e2 = 17; + tl::expected e3 = 21; + tl::expected e4 = tl::make_unexpected(42); + tl::expected e5 = tl::make_unexpected(17); + tl::expected e6 = tl::make_unexpected(21); + + e1 = e2; + REQUIRE(e1); + REQUIRE(*e1 == 17); + REQUIRE(e2); + REQUIRE(*e2 == 17); + + e1 = std::move(e2); + REQUIRE(e1); + REQUIRE(*e1 == 17); + REQUIRE(e2); + REQUIRE(*e2 == 17); + + e1 = 42; + REQUIRE(e1); + REQUIRE(*e1 == 42); + + auto unex = tl::make_unexpected(12); + e1 = unex; + REQUIRE(!e1); + REQUIRE(e1.error() == 12); + + e1 = tl::make_unexpected(42); + REQUIRE(!e1); + REQUIRE(e1.error() == 42); + + e1 = e3; + REQUIRE(e1); + REQUIRE(*e1 == 21); + + e4 = e5; + REQUIRE(!e4); + REQUIRE(e4.error() == 17); + + e4 = std::move(e6); + REQUIRE(!e4); + REQUIRE(e4.error() == 21); + + e4 = e1; + REQUIRE(e4); + REQUIRE(*e4 == 21); + +} + +TEST_CASE("Assignment deletion", "[assignment.deletion]") { + struct has_all { + has_all() = default; + has_all(const has_all&) = default; + has_all(has_all&&) noexcept = default; + has_all& operator=(const has_all&) = default; + }; + + tl::expected e1 = {}; + tl::expected e2 = {}; + e1 = e2; + + struct except_move { + except_move() = default; + except_move(const except_move&) = default; + except_move(except_move&&) noexcept(false) {}; + except_move& operator=(const except_move&) = default; + }; + tl::expected e3 = {}; + tl::expected e4 = {}; + e3 = e4; +} diff --git a/tests/emplace.cpp b/tests/emplace.cpp new file mode 100644 index 0000000..6169541 --- /dev/null +++ b/tests/emplace.cpp @@ -0,0 +1,4 @@ +#include "catch.hpp" +#include "expected.hpp" + +TEST_CASE("Emplace", "[emplace]") {