diff --git a/REUSE.toml b/REUSE.toml index de5db5e0e85..c8812eacc03 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -68,6 +68,12 @@ precedence = "closest" SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd." SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only" +[[annotations]] +path = ["src/corelib/global/patches/tlexpected/**", "src/corelib/global/qexpected_p.h"] +precedence = "closest" +SPDX-FileCopyrightText = "To the extent possible under law, Sy Brand has waived all copyright and related or neighboring rights to the expected library. This work is published from: United Kingdom." +SPDX-License-Identifier = "CC0-1.0" + [[annotations]] path = ["src/gui/doc/snippets/textdocument-images/images.qrc"] precedence = "closest" diff --git a/licenseRule.json b/licenseRule.json index 1a9c7d2afa5..e35e8ba7fd1 100644 --- a/licenseRule.json +++ b/licenseRule.json @@ -556,10 +556,20 @@ "file type": "build system", "spdx": ["BSD-3-Clause"] }, - "src/corelib/Qt6CoreConfigureFileTemplate.in": { + "src/corelib/global/qexpected_p.h": { "comment": "See REUSE.toml file", - "file type": "build system", - "spdx": ["BSD-3-Clause"] + "file type": "3rd party", + "spdx": ["CC0-1.0"] + }, + "src/corelib/global/patches/tlexpected/": { + "comment": "See REUSE.toml file", + "file type": "3rd party", + "spdx": ["CC0-1.0"] + }, + "src/corelib/Qt6CoreConfigureFileTemplate.in" : { + "comment" : "See REUSE.toml file", + "file type" : "build system", + "spdx" : ["BSD-3-Clause"] } } } diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 698f354acff..94667bc3b94 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -52,6 +52,7 @@ qt_internal_add_module(Core global/qdarwinhelpers.h global/qendian.cpp global/qendian.h global/qendian_p.h global/qexceptionhandling.h + global/qexpected_p.h global/qflags.h global/qfloat16.cpp global/qfloat16.h global/qforeach.h diff --git a/src/corelib/global/patches/tlexpected/0001-Rename-tl-unexpected-value-to-tl-unexpected-error.patch b/src/corelib/global/patches/tlexpected/0001-Rename-tl-unexpected-value-to-tl-unexpected-error.patch new file mode 100644 index 00000000000..42f5ac1c0cc --- /dev/null +++ b/src/corelib/global/patches/tlexpected/0001-Rename-tl-unexpected-value-to-tl-unexpected-error.patch @@ -0,0 +1,280 @@ +From 14c70a678a76457562a1be33d1809463dd3bf6e8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B8ger=20Hanseg=C3=A5rd?= +Date: Wed, 19 Feb 2025 12:12:29 +0100 +Subject: [PATCH 1/3] Rename tl::unexpected::value() to tl::unexpected::error() + +Fixes #149 +--- + include/tl/expected.hpp | 52 ++++++++++++++++++++--------------------- + tests/constructors.cpp | 20 ++++++++++++++++ + tests/issues.cpp | 2 +- + tests/observers.cpp | 12 ++++++++++ + tests/relops.cpp | 25 ++++++++++++++++++++ + 5 files changed, 84 insertions(+), 27 deletions(-) + +diff --git a/include/tl/expected.hpp b/include/tl/expected.hpp +index 3c22c5c..9d3a5e1 100644 +--- a/include/tl/expected.hpp ++++ b/include/tl/expected.hpp +@@ -165,10 +165,10 @@ public: + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} + +- constexpr const E &value() const & { return m_val; } +- TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } +- TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } +- constexpr const E &&value() const && { return std::move(m_val); } ++ constexpr const E &error() const & { return m_val; } ++ TL_EXPECTED_11_CONSTEXPR E &error() & { return m_val; } ++ TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(m_val); } ++ constexpr const E &&error() const && { return std::move(m_val); } + + private: + E m_val; +@@ -180,27 +180,27 @@ template unexpected(E) -> unexpected; + + template + constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { +- return lhs.value() == rhs.value(); ++ return lhs.error() == rhs.error(); + } + template + constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { +- return lhs.value() != rhs.value(); ++ return lhs.error() != rhs.error(); + } + template + constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { +- return lhs.value() < rhs.value(); ++ return lhs.error() < rhs.error(); + } + template + constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { +- return lhs.value() <= rhs.value(); ++ return lhs.error() <= rhs.error(); + } + template + constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { +- return lhs.value() > rhs.value(); ++ return lhs.error() > rhs.error(); + } + template + constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { +- return lhs.value() >= rhs.value(); ++ return lhs.error() >= rhs.error(); + } + + template +@@ -1582,7 +1582,7 @@ public: + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) +- : impl_base(unexpect, e.value()), ++ : impl_base(unexpect, e.error()), + ctor_base(detail::default_constructor_tag{}) {} + + template < +@@ -1591,7 +1591,7 @@ public: + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) +- : impl_base(unexpect, e.value()), ++ : impl_base(unexpect, e.error()), + ctor_base(detail::default_constructor_tag{}) {} + + template < +@@ -1600,7 +1600,7 @@ public: + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) +- : impl_base(unexpect, std::move(e.value())), ++ : impl_base(unexpect, std::move(e.error())), + ctor_base(detail::default_constructor_tag{}) {} + + template < +@@ -1609,7 +1609,7 @@ public: + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) +- : impl_base(unexpect, std::move(e.value())), ++ : impl_base(unexpect, std::move(e.error())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) +- detail::throw_exception(bad_expected_access(err().value())); ++ detail::throw_exception(bad_expected_access(err().error())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) +- detail::throw_exception(bad_expected_access(err().value())); ++ detail::throw_exception(bad_expected_access(err().error())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) +- detail::throw_exception(bad_expected_access(std::move(err()).value())); ++ detail::throw_exception(bad_expected_access(std::move(err()).error())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) +- detail::throw_exception(bad_expected_access(std::move(err()).value())); ++ detail::throw_exception(bad_expected_access(std::move(err()).error())); + return std::move(val()); + } + + constexpr const E &error() const & { + TL_ASSERT(!has_value()); +- return err().value(); ++ return err().error(); + } + TL_EXPECTED_11_CONSTEXPR E &error() & { + TL_ASSERT(!has_value()); +- return err().value(); ++ return err().error(); + } + constexpr const E &&error() const && { + TL_ASSERT(!has_value()); +- return std::move(err().value()); ++ return std::move(err().error()); + } + TL_EXPECTED_11_CONSTEXPR E &&error() && { + TL_ASSERT(!has_value()); +- return std::move(err().value()); ++ return std::move(err().error()); + } + + template constexpr T value_or(U &&v) const & { +@@ -2446,19 +2446,19 @@ constexpr bool operator!=(const U &v, const expected &x) { + + template + constexpr bool operator==(const expected &x, const unexpected &e) { +- return x.has_value() ? false : x.error() == e.value(); ++ return x.has_value() ? false : x.error() == e.error(); + } + template + constexpr bool operator==(const unexpected &e, const expected &x) { +- return x.has_value() ? false : x.error() == e.value(); ++ return x.has_value() ? false : x.error() == e.error(); + } + template + constexpr bool operator!=(const expected &x, const unexpected &e) { +- return x.has_value() ? true : x.error() != e.value(); ++ return x.has_value() ? true : x.error() != e.error(); + } + template + constexpr bool operator!=(const unexpected &e, const expected &x) { +- return x.has_value() ? true : x.error() != e.value(); ++ return x.has_value() ? true : x.error() != e.error(); + } + + template u('s'); ++ tl::expected e(u); ++ REQUIRE(e.error() == 's'); ++ } ++ ++ { ++ struct value { ++ constexpr explicit value(char v) : val(v) {} ++ char val; ++ }; ++ ++ constexpr tl::unexpected u('s'); ++ tl::expected e1(u); ++ REQUIRE(e1.error().val == 's'); ++ ++ tl::expected e2(tl::unexpected('s')); ++ REQUIRE(e2.error().val == 's'); ++ } + } +diff --git a/tests/issues.cpp b/tests/issues.cpp +index f929099..9efe14b 100644 +--- a/tests/issues.cpp ++++ b/tests/issues.cpp +@@ -166,7 +166,7 @@ TEST_CASE("Issue 122", "[issues.122]") { + #ifdef __cpp_deduction_guides + TEST_CASE("Issue 89", "[issues.89]") { + auto s = tl::unexpected("Some string"); +- REQUIRE(s.value() == std::string("Some string")); ++ REQUIRE(s.error() == std::string("Some string")); + } + #endif + +diff --git a/tests/observers.cpp b/tests/observers.cpp +index 5d8473c..cff3e9f 100644 +--- a/tests/observers.cpp ++++ b/tests/observers.cpp +@@ -34,3 +34,15 @@ TEST_CASE("Observers", "[observers]") { + REQUIRE(o4->been_moved); + REQUIRE(!o5.been_moved); + } ++ ++#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++TEST_CASE("Observers invalid access", "[observers]") { ++ tl::expected err(tl::make_unexpected('!')); ++ ++ REQUIRE_THROWS_AS(err.value(), tl::bad_expected_access); ++ REQUIRE_THROWS_AS(std::as_const(err).value(), tl::bad_expected_access); ++ REQUIRE_THROWS_AS(std::move(std::as_const(err)).value(), ++ tl::bad_expected_access); ++ REQUIRE_THROWS_AS(std::move(err).value(), tl::bad_expected_access); ++} ++#endif +diff --git a/tests/relops.cpp b/tests/relops.cpp +index 99dee5f..b873b6a 100644 +--- a/tests/relops.cpp ++++ b/tests/relops.cpp +@@ -15,3 +15,28 @@ TEST_CASE("Relational operators", "[relops]") { + + REQUIRE(o6 == o6); + } ++ ++TEST_CASE("Relational operators unexpected", "[relops]") { ++ tl::unexpected zero(0); ++ tl::unexpected one(1); ++ ++ REQUIRE(one == one); ++ REQUIRE(one != zero); ++ REQUIRE(zero < one); ++ REQUIRE(zero <= one); ++ REQUIRE(one <= one); ++ REQUIRE(one > zero); ++ REQUIRE(one >= zero); ++ REQUIRE(one >= one); ++} ++ ++TEST_CASE("Relational operators expected vs unexpected", "[relops]") { ++ tl::expected ezero(tl::unexpect, 0); ++ tl::unexpected uzero(0); ++ tl::unexpected uone(1); ++ ++ REQUIRE(ezero == uzero); ++ REQUIRE(ezero != uone); ++ REQUIRE(uzero == ezero); ++ REQUIRE(uone != ezero); ++} +-- +2.44.0.windows.1 + diff --git a/src/corelib/global/patches/tlexpected/0001-tl-expected-disable-exceptions-with-qt-s-macros.patch b/src/corelib/global/patches/tlexpected/0001-tl-expected-disable-exceptions-with-qt-s-macros.patch new file mode 100644 index 00000000000..19e889d5d5c --- /dev/null +++ b/src/corelib/global/patches/tlexpected/0001-tl-expected-disable-exceptions-with-qt-s-macros.patch @@ -0,0 +1,36 @@ +From 6aefb01b0f1547d05dd7eaa549c6f2f435a3b468 Mon Sep 17 00:00:00 2001 +From: Tim Blechmann +Date: Sun, 11 May 2025 10:26:58 +0200 +Subject: [PATCH] tl::expected: disable exceptions with qt's macros + +Change-Id: Ifee3f2800e03f7ecd5c9c218acfd3d7359cd6919 +--- + src/corelib/global/qexpected_p.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/corelib/global/qexpected_p.h b/src/corelib/global/qexpected_p.h +index a54aea1f7d7..45c6196c947 100644 +--- a/src/corelib/global/qexpected_p.h ++++ b/src/corelib/global/qexpected_p.h +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -46,6 +47,10 @@ + #define TL_EXPECTED_EXCEPTIONS_ENABLED + #endif + ++#if defined(TL_EXPECTED_EXCEPTIONS_ENABLED) && defined(QT_NO_EXCEPTIONS) ++# undef TL_EXPECTED_EXCEPTIONS_ENABLED ++#endif ++ + #if (defined(_MSC_VER) && _MSC_VER == 1900) + #define TL_EXPECTED_MSVC2015 + #define TL_EXPECTED_MSVC2015_CONSTEXPR +-- +2.49.0 + diff --git a/src/corelib/global/patches/tlexpected/0002-Require-in_place_t-with-variadic-unexpected-ctors.patch b/src/corelib/global/patches/tlexpected/0002-Require-in_place_t-with-variadic-unexpected-ctors.patch new file mode 100644 index 00000000000..88a66f81f84 --- /dev/null +++ b/src/corelib/global/patches/tlexpected/0002-Require-in_place_t-with-variadic-unexpected-ctors.patch @@ -0,0 +1,311 @@ +From c6064c91aa84412d07a4f3948d19496e0f294cef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B8ger=20Hanseg=C3=A5rd?= +Date: Wed, 19 Feb 2025 14:05:45 +0100 +Subject: [PATCH 2/3] Require in_place_t with variadic unexpected ctors + +This makes unexpected ctors more consistent with the C++ standard +--- + include/tl/expected.hpp | 31 +++++----- + tests/constructors.cpp | 123 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 140 insertions(+), 14 deletions(-) + +diff --git a/include/tl/expected.hpp b/include/tl/expected.hpp +index 9d3a5e1..83920bd 100644 +--- a/include/tl/expected.hpp ++++ b/include/tl/expected.hpp +@@ -156,13 +156,15 @@ public: + + template ::value>::type * = nullptr> +- constexpr explicit unexpected(Args &&...args) ++ constexpr explicit unexpected(in_place_t, Args &&...args) + : m_val(std::forward(args)...) {} ++ + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> +- constexpr explicit unexpected(std::initializer_list l, Args &&...args) ++ constexpr explicit unexpected(in_place_t, std::initializer_list l, ++ Args &&...args) + : m_val(l, std::forward(args)...) {} + + constexpr const E &error() const & { return m_val; } +@@ -471,7 +473,7 @@ struct expected_storage_base { + detail::enable_if_t::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) +- : m_unexpect(std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template il, + Args &&...args) +- : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { +@@ -518,7 +520,7 @@ template struct expected_storage_base { + detail::enable_if_t::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) +- : m_unexpect(std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template struct expected_storage_base { + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) +- : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; +@@ -563,7 +565,7 @@ template struct expected_storage_base { + detail::enable_if_t::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) +- : m_unexpect(std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template struct expected_storage_base { + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) +- : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; +@@ -612,7 +614,7 @@ template struct expected_storage_base { + detail::enable_if_t::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) +- : m_unexpect(std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template struct expected_storage_base { + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) +- : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, il, std::forward(args)...), ++ m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; +@@ -656,7 +659,7 @@ template struct expected_storage_base { + detail::enable_if_t::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) +- : m_unexpect(std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template struct expected_storage_base { + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) +- : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; +@@ -690,7 +693,7 @@ template struct expected_storage_base { + detail::enable_if_t::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) +- : m_unexpect(std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template struct expected_storage_base { + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) +- : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ++ : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; +diff --git a/tests/constructors.cpp b/tests/constructors.cpp +index 10b96de..f24a46a 100644 +--- a/tests/constructors.cpp ++++ b/tests/constructors.cpp +@@ -3,6 +3,7 @@ + + #include + #include ++#include + #include + + struct takes_init_and_variadic { +@@ -13,6 +14,27 @@ struct takes_init_and_variadic { + : v(l), t(std::forward(args)...) {} + }; + ++struct trivial_type { ++ int x; ++ int y; ++ trivial_type(int _x, int _y) : x{_x}, y{_y} {} ++ trivial_type(std::initializer_list list) { ++ auto it = list.begin(); ++ x = *it; ++ ++it; ++ y = *it; ++ } ++}; ++ ++struct takes_init_and_variadic_trivial { ++ trivial_type p; ++ std::tuple t; ++ template ++ takes_init_and_variadic_trivial(std::initializer_list l, ++ Args &&...args) ++ : p(l), t(std::forward(args)...) {} ++}; ++ + TEST_CASE("Constructors", "[constructors]") { + { + tl::expected e; +@@ -38,6 +60,12 @@ TEST_CASE("Constructors", "[constructors]") { + REQUIRE(e == 42); + } + ++ { ++ tl::expected e(tl::in_place); ++ REQUIRE(e); ++ REQUIRE(e == 0); ++ } ++ + { + tl::expected,int> e (tl::in_place, {0,1}); + REQUIRE(e); +@@ -52,6 +80,40 @@ TEST_CASE("Constructors", "[constructors]") { + REQUIRE(std::get<1>(*e) == 1); + } + ++ { ++ tl::expected> e(tl::unexpect, 0, 1); ++ REQUIRE(!e); ++ REQUIRE(std::get<0>(e.error()) == 0); ++ REQUIRE(std::get<1>(e.error()) == 1); ++ } ++ ++ { ++ tl::expected> e(tl::unexpect, 0, 1); ++ REQUIRE(!e); ++ REQUIRE(std::get<0>(e.error()) == 0); ++ REQUIRE(std::get<1>(e.error()) == 1); ++ } ++ { ++ tl::expected> e(tl::unexpect, 2, 1); ++ REQUIRE(!e); ++ REQUIRE(e.error()[0] == 1); ++ REQUIRE(e.error()[1] == 1); ++ } ++ { ++ tl::expected, std::vector> e(tl::unexpect, 2, 1); ++ REQUIRE(!e); ++ REQUIRE(e.error()[0] == 1); ++ REQUIRE(e.error()[1] == 1); ++ } ++ { ++ tl::expected, takes_init_and_variadic> e(tl::unexpect, ++ {0, 1}, 2, 3); ++ REQUIRE(!e); ++ REQUIRE(e.error().v[0] == 0); ++ REQUIRE(e.error().v[1] == 1); ++ REQUIRE(std::get<0>(e.error().t) == 2); ++ REQUIRE(std::get<1>(e.error().t) == 3); ++ } + { + tl::expected e (tl::in_place, {0,1}, 2, 3); + REQUIRE(e); +@@ -60,6 +122,59 @@ TEST_CASE("Constructors", "[constructors]") { + REQUIRE(std::get<0>(e->t) == 2); + REQUIRE(std::get<1>(e->t) == 3); + } ++ { ++ tl::expected e(tl::unexpect, {0, 1}, 2, 3); ++ REQUIRE(!e); ++ REQUIRE(e.error().v[0] == 0); ++ REQUIRE(e.error().v[1] == 1); ++ REQUIRE(std::get<0>(e.error().t) == 2); ++ REQUIRE(std::get<1>(e.error().t) == 3); ++ } ++ { ++ tl::expected e(tl::unexpect, {0, 1}, 2, 3); ++ REQUIRE(!e); ++ REQUIRE(e.error().p.x == 0); ++ REQUIRE(e.error().p.y == 1); ++ REQUIRE(std::get<0>(e.error().t) == 2); ++ REQUIRE(std::get<1>(e.error().t) == 3); ++ } ++ { ++ tl::expected e(tl::unexpect, ++ {0, 1}, 2, 3); ++ REQUIRE(!e); ++ REQUIRE(e.error().p.x == 0); ++ REQUIRE(e.error().p.y == 1); ++ REQUIRE(std::get<0>(e.error().t) == 2); ++ REQUIRE(std::get<1>(e.error().t) == 3); ++ } ++ { ++ tl::expected> e(tl::unexpect, 2, 1); ++ REQUIRE(!e); ++ REQUIRE(e.error()[0] == 1); ++ REQUIRE(e.error()[1] == 1); ++ } ++ { ++ tl::expected, trivial_type> e(tl::unexpect, 1, 2); ++ REQUIRE(!e); ++ REQUIRE(e.error().x == 1); ++ REQUIRE(e.error().y == 2); ++ } ++ { ++ tl::expected, takes_init_and_variadic_trivial> e(tl::unexpect, {1, 2}, 3, 4); ++ REQUIRE(!e); ++ REQUIRE(e.error().p.x == 1); ++ REQUIRE(e.error().p.y == 2); ++ REQUIRE(std::get<0>(e.error().t) == 3); ++ REQUIRE(std::get<1>(e.error().t) == 4); ++ } ++ { ++ tl::expected e(tl::unexpect, {0, 1}, 2, 3); ++ REQUIRE(!e); ++ REQUIRE(e.error().v[0] == 0); ++ REQUIRE(e.error().v[1] == 1); ++ REQUIRE(std::get<0>(e.error().t) == 2); ++ REQUIRE(std::get<1>(e.error().t) == 3); ++ } + + { + tl::expected e; +@@ -152,3 +267,11 @@ TEST_CASE("Constructors", "[constructors]") { + REQUIRE(e2.error().val == 's'); + } + } ++ ++TEST_CASE("Unexpected constructors", "[constructors]") { ++ REQUIRE(tl::unexpected(1).error() == 1); ++ REQUIRE(tl::unexpected(tl::in_place).error() == 0); ++ REQUIRE(tl::unexpected(tl::in_place, 1).error() == 1); ++ REQUIRE(tl::unexpected>(tl::in_place, {1, 2, 3}).error() == ++ std::vector{1, 2, 3}); ++} +-- +2.44.0.windows.1 + diff --git a/src/corelib/global/patches/tlexpected/0003-Adapt-tl-expected-to-q23-expected.patch b/src/corelib/global/patches/tlexpected/0003-Adapt-tl-expected-to-q23-expected.patch new file mode 100644 index 00000000000..32c4e6c77c4 --- /dev/null +++ b/src/corelib/global/patches/tlexpected/0003-Adapt-tl-expected-to-q23-expected.patch @@ -0,0 +1,106 @@ +From d99fc8961341d494597a62f5513573660a7f076f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B8ger=20Hanseg=C3=A5rd?= +Date: Tue, 18 Feb 2025 16:07:45 +0100 +Subject: [PATCH 3/3] Adapt tl::expected to q23::expected + +--- + include/tl/{expected.hpp => qexpected_p.h} | 33 ++++++++++++++++++---- + 1 file changed, 27 insertions(+), 6 deletions(-) + rename include/tl/{expected.hpp => qexpected_p.h} (99%) + +diff --git a/include/tl/expected.hpp b/include/tl/qexpected_p.h +similarity index 99% +rename from include/tl/expected.hpp +rename to include/tl/qexpected_p.h +index 83920bd..a54aea1 100644 +--- a/include/tl/expected.hpp ++++ b/include/tl/qexpected_p.h +@@ -16,15 +16,32 @@ + #ifndef TL_EXPECTED_HPP + #define TL_EXPECTED_HPP + ++// ++// W A R N I N G ++// ------------- ++// ++// This file is not part of the Qt API. It exists purely as an ++// implementation detail. This header file may change from version to ++// version without notice, or even be removed. ++// ++// We mean it. ++// ++ + #define TL_EXPECTED_VERSION_MAJOR 1 + #define TL_EXPECTED_VERSION_MINOR 1 + #define TL_EXPECTED_VERSION_PATCH 0 + ++#include ++#include ++#include ++ + #include + #include + #include + #include + ++#define TL_ASSERT Q_ASSERT ++ + #if defined(__EXCEPTIONS) || defined(_CPPUNWIND) + #define TL_EXPECTED_EXCEPTIONS_ENABLED + #endif +@@ -81,7 +98,8 @@ + #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 { ++QT_BEGIN_NAMESPACE ++namespace q23 { + namespace detail { + template + struct is_trivially_copy_constructible +@@ -91,11 +109,12 @@ template + struct is_trivially_copy_constructible> : std::false_type {}; + #endif + } // namespace detail +-} // namespace tl ++} // namespace q23 ++QT_END_NAMESPACE + #endif + + #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ +- tl::detail::is_trivially_copy_constructible ++ q23::detail::is_trivially_copy_constructible + #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable + #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ +@@ -132,7 +151,8 @@ struct is_trivially_copy_constructible> : std::false_type {}; + #define TL_EXPECTED_11_CONSTEXPR constexpr + #endif + +-namespace tl { ++QT_BEGIN_NAMESPACE ++namespace q23 { + template class expected; + + #ifndef TL_MONOSTATE_INPLACE_MUTEX +@@ -396,7 +416,7 @@ struct is_nothrow_swappable + #endif + #endif + +-// Trait for checking if a type is a tl::expected ++// Trait for checking if a type is a q23::expected + template struct is_expected_impl : std::false_type {}; + template + struct is_expected_impl> : std::true_type {}; +@@ -2474,6 +2494,7 @@ void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); + } +-} // namespace tl ++} // namespace q23 ++QT_END_NAMESPACE + + #endif +-- +2.44.0.windows.1 + diff --git a/src/corelib/global/patches/tlexpected/0004-Prefix-SPDX.patch b/src/corelib/global/patches/tlexpected/0004-Prefix-SPDX.patch new file mode 100644 index 00000000000..882bdb1acd0 --- /dev/null +++ b/src/corelib/global/patches/tlexpected/0004-Prefix-SPDX.patch @@ -0,0 +1,11 @@ +diff --git a/src/corelib/global/qexpected_p.h b/src/corelib/global/qexpected_p.h +index 45c6196c947..1c9b37ae82f 100644 +--- a/src/corelib/global/qexpected_p.h ++++ b/src/corelib/global/qexpected_p.h +@@ -1,3 +1,6 @@ ++// Copyright (C) 2017 Sy Brand (tartanllama@gmail.com, @TartanLlama) ++// SPDX-License-Identifier: CC0-1.0 ++ + /// + // expected - An implementation of std::expected with extensions + // Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) diff --git a/src/corelib/global/qexpected_p.h b/src/corelib/global/qexpected_p.h new file mode 100644 index 00000000000..1c9b37ae82f --- /dev/null +++ b/src/corelib/global/qexpected_p.h @@ -0,0 +1,2508 @@ +// Copyright (C) 2017 Sy Brand (tartanllama@gmail.com, @TartanLlama) +// SPDX-License-Identifier: CC0-1.0 + +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 1 +#define TL_EXPECTED_VERSION_PATCH 0 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TL_ASSERT Q_ASSERT + +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if defined(TL_EXPECTED_EXCEPTIONS_ENABLED) && defined(QT_NO_EXCEPTIONS) +# undef TL_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC55 +#endif + +#if !defined(TL_ASSERT) +//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug +#if (TL_CPLUSPLUS > 201103L) && !defined(TL_EXPECTED_GCC49) +#include +#define TL_ASSERT(x) assert(x) +#else +#define TL_ASSERT(x) +#endif +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions + +#define TL_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// This one will be different for GCC 5.7 if it's ever supported +#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__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +QT_BEGIN_NAMESPACE +namespace q23 { +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace q23 +QT_END_NAMESPACE +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + q23::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif + +#ifdef _MSVC_LANG +#define TL_CPLUSPLUS _MSVC_LANG +#else +#define TL_CPLUSPLUS __cplusplus +#endif + +#if TL_CPLUSPLUS > 201103L +#define TL_EXPECTED_CXX14 +#endif + +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif + +#if (TL_CPLUSPLUS == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) +#define TL_EXPECTED_11_CONSTEXPR +#else +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif + +QT_BEGIN_NAMESPACE +namespace q23 { +template class expected; + +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +class monostate {}; + +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif + +template class unexpected { +public: + static_assert(!std::is_same::value, "E must not be void"); + + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} + + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + + template ::value>::type * = nullptr> + constexpr explicit unexpected(in_place_t, Args &&...args) + : m_val(std::forward(args)...) {} + + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(in_place_t, std::initializer_list l, + Args &&...args) + : m_val(l, std::forward(args)...) {} + + constexpr const E &error() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &error() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(m_val); } + constexpr const E &&error() const && { return std::move(m_val); } + +private: + E m_val; +}; + +#ifdef __cpp_deduction_guides +template unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.error() == rhs.error(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.error() != rhs.error(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.error() < rhs.error(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.error() <= rhs.error(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.error() > rhs.error(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.error() >= rhs.error(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(e); +#else + (void)e; +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif +#endif +} + +#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_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : std::true_type {}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template < + typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a q23::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // namespace detail + +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; + +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template ::value, + bool = std::is_trivially_destructible::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; + expected_storage_base &operator=(const expected_storage_base &) = default; + expected_storage_base &operator=(expected_storage_base &&) = default; + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; + expected_storage_base &operator=(const expected_storage_base &) = default; + expected_storage_base &operator=(expected_storage_base &&) = default; + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(in_place, il, std::forward(args)...), + m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; + expected_storage_base &operator=(const expected_storage_base &) = default; + expected_storage_base &operator=(expected_storage_base &&) = default; + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #if __GNUC__ <= 5 + //no constexpr for GCC 4/5 bug + #else + TL_EXPECTED_MSVC2015_CONSTEXPR + #endif + expected_storage_base() : m_has_val(true) {} + + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; + expected_storage_base &operator=(const expected_storage_base &) = default; + expected_storage_base &operator=(expected_storage_base &&) = default; + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(in_place, std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(in_place, il, std::forward(args)...), m_has_val(false) {} + + expected_storage_base(const expected_storage_base &) = default; + expected_storage_base(expected_storage_base &&) = default; + expected_storage_base &operator=(const expected_storage_base &) = default; + expected_storage_base &operator=(expected_storage_base &&) = default; + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } + + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(rhs.get()); +#endif + } else { + assign_common(rhs); + } + } + + // These overloads do the same as above, but for rvalues + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); +#endif + } else { + assign_common(std::move(rhs)); + } + } + +#else + + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } +#endif + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct() noexcept { this->m_has_val = true; } + + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op + } +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value, + bool = (is_copy_constructible_or_void::value && + std::is_copy_constructible::value)> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are non-trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_EXPECTED_GCC49 +template >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; + + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; + + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) + : expected_copy_base(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template >::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value, + bool = (is_copy_constructible_or_void::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value)> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; + + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; + + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base & + operator=(expected_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_EXPECTED_GCC49 +template , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; + + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base(expected_move_assign_base &&rhs) = default; + + expected_move_assign_base & + operator=(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; + +// expected_default_ctor_base will ensure that expected has a deleted default +// constructor if T is not default constructible. +// This specialization is for when T is default constructible +template ::value || std::is_void::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +// This specialization is for when T is not default constructible +template struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; +} // namespace detail + +template class bad_expected_access : public std::exception { +public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} + + virtual const char *what() const noexcept override { + return "Bad expected access"; + } + + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } + +private: + E m_val; +}; + +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. +template +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::value, "E must not be a reference"); + + 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); + } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + 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)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; + + template ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.error()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.error()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.error())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.error())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template < + class U, class G, + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + 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> + 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; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + 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> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::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 { + this->destroy_val(); + ::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(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + 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(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; +#endif + } + } + +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 + } + + 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, typename std::is_nothrow_move_constructible::type{}, + typename 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(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::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); + } catch (...) { + val() = std::move(temp); + throw; + } +#else + ::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); +#endif + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(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 + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + +public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + 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()) { + swap_where_both_have_value(rhs, typename 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, typename std::is_void::type{}); + } else { + using std::swap; + swap(err(), rhs.err()); + } + } + + constexpr const T *operator->() const { + TL_ASSERT(has_value()); + return valptr(); + } + TL_EXPECTED_11_CONSTEXPR T *operator->() { + TL_ASSERT(has_value()); + return valptr(); + } + + template ::value> * = nullptr> + constexpr const U &operator*() const & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + TL_ASSERT(has_value()); + return std::move(val()); + } + + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().error())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().error())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).error())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).error())); + return std::move(val()); + } + + constexpr const E &error() const & { + TL_ASSERT(!has_value()); + return err().error(); + } + TL_EXPECTED_11_CONSTEXPR E &error() & { + TL_ASSERT(!has_value()); + return err().error(); + } + constexpr const E &&error() const && { + TL_ASSERT(!has_value()); + return std::move(err().error()); + } + TL_EXPECTED_11_CONSTEXPR E &&error() && { + TL_ASSERT(!has_value()); + return std::move(err().error()); + } + + 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)); + } + 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)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_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 >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_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()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::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()); +} + +template (), + std::declval().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)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::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()); +} + +template (), + std::declval().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)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? 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()) + ? 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()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +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() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.error(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.error(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.error(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.error(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace q23 +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/global/qt_attribution.json b/src/corelib/global/qt_attribution.json new file mode 100644 index 00000000000..22379e26191 --- /dev/null +++ b/src/corelib/global/qt_attribution.json @@ -0,0 +1,14 @@ +{ + "Id": "tlexpected", + "Name": "tl::expected", + "QDocModule": "qtcore", + "QtUsage": "Available as a private type in all Qt modules", + "Description": "Single header implementation of std::expected with functional-style extensions.", + "Files": [ "qexpected_p.h" ], + "Homepage": "https://github.com/TartanLlama/expected/", + "Version": "41d3e1f48d682992a2230b2a715bca38b848b269", + "DownloadLocation": "https://github.com/TartanLlama/expected/blob/41d3e1f48d682992a2230b2a715bca38b848b269/include/tl/expected.hpp", + "License": "Creative Commons Zero v1.0 Universal", + "LicenseId": "CC0-1.0", + "Copyright": "To the extent possible under law, Sy Brand has waived all copyright and related or neighboring rights to the expected library. This work is published from: United Kingdom." +}