Add q23::expected as a private type

This patch adds a std::expected-like type in Qt for use in the Qt
implementation. It is using Sy Brand's tl::expected which is similar to
std::expected, and works with C++17. Although it closely resembles
std::expected, it is not identical and is therefor added as a private
header. The new type is intended to be accessible to all Qt modules and
is therefore made available through Qt Core. It is, however, not
intended for use in public APIs.

[ChangeLog][Third-Party Code] Added Sy Brand's tl::expected as a third
party dependency for use internally in Qt implementation.

Change-Id: I09930f31bf97498643d62814c688f288d5c33265
Reviewed-by: Tim Blechmann <tim.blechmann@qt.io>
This commit is contained in:
Tim Blechmann 2025-05-08 19:05:17 +02:00 committed by Volker Hilsheimer
parent 60f36b8d02
commit 6f319847d0
10 changed files with 3286 additions and 3 deletions

View File

@ -68,6 +68,12 @@ precedence = "closest"
SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd." SPDX-FileCopyrightText = "Copyright (C) 2024 The Qt Company Ltd."
SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only" 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]] [[annotations]]
path = ["src/gui/doc/snippets/textdocument-images/images.qrc"] path = ["src/gui/doc/snippets/textdocument-images/images.qrc"]
precedence = "closest" precedence = "closest"

View File

@ -556,10 +556,20 @@
"file type": "build system", "file type": "build system",
"spdx": ["BSD-3-Clause"] "spdx": ["BSD-3-Clause"]
}, },
"src/corelib/Qt6CoreConfigureFileTemplate.in": { "src/corelib/global/qexpected_p.h": {
"comment": "See REUSE.toml file", "comment": "See REUSE.toml file",
"file type": "build system", "file type": "3rd party",
"spdx": ["BSD-3-Clause"] "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"]
} }
} }
} }

View File

@ -52,6 +52,7 @@ qt_internal_add_module(Core
global/qdarwinhelpers.h global/qdarwinhelpers.h
global/qendian.cpp global/qendian.h global/qendian_p.h global/qendian.cpp global/qendian.h global/qendian_p.h
global/qexceptionhandling.h global/qexceptionhandling.h
global/qexpected_p.h
global/qflags.h global/qflags.h
global/qfloat16.cpp global/qfloat16.h global/qfloat16.cpp global/qfloat16.h
global/qforeach.h global/qforeach.h

View File

@ -0,0 +1,280 @@
From 14c70a678a76457562a1be33d1809463dd3bf6e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B8ger=20Hanseg=C3=A5rd?= <joger.hansegard@qt.io>
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<U> l, Args &&...args)
: m_val(l, std::forward<Args>(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 <class E> unexpected(E) -> unexpected<E>;
template <class E>
constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
- return lhs.value() == rhs.value();
+ return lhs.error() == rhs.error();
}
template <class E>
constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
- return lhs.value() != rhs.value();
+ return lhs.error() != rhs.error();
}
template <class E>
constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
- return lhs.value() < rhs.value();
+ return lhs.error() < rhs.error();
}
template <class E>
constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
- return lhs.value() <= rhs.value();
+ return lhs.error() <= rhs.error();
}
template <class E>
constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
- return lhs.value() > rhs.value();
+ return lhs.error() > rhs.error();
}
template <class E>
constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
- return lhs.value() >= rhs.value();
+ return lhs.error() >= rhs.error();
}
template <class E>
@@ -1582,7 +1582,7 @@ public:
detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
nullptr>
explicit constexpr expected(const unexpected<G> &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<std::is_convertible<const G &, E>::value> * = nullptr>
constexpr expected(unexpected<G> 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<!std::is_convertible<G &&, E>::value> * = nullptr>
explicit constexpr expected(unexpected<G> &&e) noexcept(
std::is_nothrow_constructible<E, G &&>::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<std::is_convertible<G &&, E>::value> * = nullptr>
constexpr expected(unexpected<G> &&e) noexcept(
std::is_nothrow_constructible<E, G &&>::value)
- : impl_base(unexpect, std::move(e.value())),
+ : impl_base(unexpect, std::move(e.error())),
ctor_base(detail::default_constructor_tag{}) {}
template <class... Args,
@@ -2017,46 +2017,46 @@ public:
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
TL_EXPECTED_11_CONSTEXPR const U &value() const & {
if (!has_value())
- detail::throw_exception(bad_expected_access<E>(err().value()));
+ detail::throw_exception(bad_expected_access<E>(err().error()));
return val();
}
template <class U = T,
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
TL_EXPECTED_11_CONSTEXPR U &value() & {
if (!has_value())
- detail::throw_exception(bad_expected_access<E>(err().value()));
+ detail::throw_exception(bad_expected_access<E>(err().error()));
return val();
}
template <class U = T,
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
if (!has_value())
- detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+ detail::throw_exception(bad_expected_access<E>(std::move(err()).error()));
return std::move(val());
}
template <class U = T,
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
TL_EXPECTED_11_CONSTEXPR U &&value() && {
if (!has_value())
- detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+ detail::throw_exception(bad_expected_access<E>(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 <class U> constexpr T value_or(U &&v) const & {
@@ -2446,19 +2446,19 @@ constexpr bool operator!=(const U &v, const expected<T, E> &x) {
template <class T, class E>
constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
- return x.has_value() ? false : x.error() == e.value();
+ return x.has_value() ? false : x.error() == e.error();
}
template <class T, class E>
constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
- return x.has_value() ? false : x.error() == e.value();
+ return x.has_value() ? false : x.error() == e.error();
}
template <class T, class E>
constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
- return x.has_value() ? true : x.error() != e.value();
+ return x.has_value() ? true : x.error() != e.error();
}
template <class T, class E>
constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
- return x.has_value() ? true : x.error() != e.value();
+ return x.has_value() ? true : x.error() != e.error();
}
template <class T, class E,
diff --git a/tests/constructors.cpp b/tests/constructors.cpp
index df168ba..10b96de 100644
--- a/tests/constructors.cpp
+++ b/tests/constructors.cpp
@@ -131,4 +131,24 @@ TEST_CASE("Constructors", "[constructors]") {
REQUIRE(!e);
REQUIRE(e.error() == 42);
}
+
+ {
+ constexpr tl::unexpected<char> u('s');
+ tl::expected<int, int> e(u);
+ REQUIRE(e.error() == 's');
+ }
+
+ {
+ struct value {
+ constexpr explicit value(char v) : val(v) {}
+ char val;
+ };
+
+ constexpr tl::unexpected<char> u('s');
+ tl::expected<int, value> e1(u);
+ REQUIRE(e1.error().val == 's');
+
+ tl::expected<int, value> e2(tl::unexpected<char>('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<int, char> err(tl::make_unexpected('!'));
+
+ REQUIRE_THROWS_AS(err.value(), tl::bad_expected_access<char>);
+ REQUIRE_THROWS_AS(std::as_const(err).value(), tl::bad_expected_access<char>);
+ REQUIRE_THROWS_AS(std::move(std::as_const(err)).value(),
+ tl::bad_expected_access<char>);
+ REQUIRE_THROWS_AS(std::move(err).value(), tl::bad_expected_access<char>);
+}
+#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<int> zero(0);
+ tl::unexpected<int> 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<int, int> ezero(tl::unexpect, 0);
+ tl::unexpected<int> uzero(0);
+ tl::unexpected<int> uone(1);
+
+ REQUIRE(ezero == uzero);
+ REQUIRE(ezero != uone);
+ REQUIRE(uzero == ezero);
+ REQUIRE(uone != ezero);
+}
--
2.44.0.windows.1

View File

@ -0,0 +1,36 @@
From 6aefb01b0f1547d05dd7eaa549c6f2f435a3b468 Mon Sep 17 00:00:00 2001
From: Tim Blechmann <tim.blechmann@qt.io>
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 <QtCore/private/qglobal_p.h>
#include <QtCore/qassert.h>
#include <QtCore/qtconfigmacros.h>
+#include <QtCore/qconfig.h>
#include <exception>
#include <functional>
@@ -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

View File

@ -0,0 +1,311 @@
From c6064c91aa84412d07a4f3948d19496e0f294cef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B8ger=20Hanseg=C3=A5rd?= <joger.hansegard@qt.io>
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 <class... Args, typename std::enable_if<std::is_constructible<
E, Args &&...>::value>::type * = nullptr>
- constexpr explicit unexpected(Args &&...args)
+ constexpr explicit unexpected(in_place_t, Args &&...args)
: m_val(std::forward<Args>(args)...) {}
+
template <
class U, class... Args,
typename std::enable_if<std::is_constructible<
E, std::initializer_list<U> &, Args &&...>::value>::type * = nullptr>
- constexpr explicit unexpected(std::initializer_list<U> l, Args &&...args)
+ constexpr explicit unexpected(in_place_t, std::initializer_list<U> l,
+ Args &&...args)
: m_val(l, std::forward<Args>(args)...) {}
constexpr const E &error() const & { return m_val; }
@@ -471,7 +473,7 @@ struct expected_storage_base {
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
nullptr>
constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
- : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, std::forward<Args>(args)...), m_has_val(false) {}
template <class U, class... Args,
detail::enable_if_t<std::is_constructible<
@@ -479,7 +481,7 @@ struct expected_storage_base {
constexpr explicit expected_storage_base(unexpect_t,
std::initializer_list<U> il,
Args &&...args)
- : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, il, std::forward<Args>(args)...), m_has_val(false) {}
~expected_storage_base() {
if (m_has_val) {
@@ -518,7 +520,7 @@ template <class T, class E> struct expected_storage_base<T, E, true, true> {
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
nullptr>
constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
- : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, std::forward<Args>(args)...), m_has_val(false) {}
template <class U, class... Args,
detail::enable_if_t<std::is_constructible<
@@ -526,7 +528,7 @@ template <class T, class E> struct expected_storage_base<T, E, true, true> {
constexpr explicit expected_storage_base(unexpect_t,
std::initializer_list<U> il,
Args &&...args)
- : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, il, std::forward<Args>(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 <class T, class E> struct expected_storage_base<T, E, true, false> {
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
nullptr>
constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
- : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, std::forward<Args>(args)...), m_has_val(false) {}
template <class U, class... Args,
detail::enable_if_t<std::is_constructible<
@@ -571,7 +573,7 @@ template <class T, class E> struct expected_storage_base<T, E, true, false> {
constexpr explicit expected_storage_base(unexpect_t,
std::initializer_list<U> il,
Args &&...args)
- : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, il, std::forward<Args>(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 <class T, class E> struct expected_storage_base<T, E, false, true> {
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
nullptr>
constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
- : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, std::forward<Args>(args)...), m_has_val(false) {}
template <class U, class... Args,
detail::enable_if_t<std::is_constructible<
@@ -620,7 +622,8 @@ template <class T, class E> struct expected_storage_base<T, E, false, true> {
constexpr explicit expected_storage_base(unexpect_t,
std::initializer_list<U> il,
Args &&...args)
- : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, il, std::forward<Args>(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 <class E> struct expected_storage_base<void, E, false, true> {
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
nullptr>
constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
- : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, std::forward<Args>(args)...), m_has_val(false) {}
template <class U, class... Args,
detail::enable_if_t<std::is_constructible<
@@ -664,7 +667,7 @@ template <class E> struct expected_storage_base<void, E, false, true> {
constexpr explicit expected_storage_base(unexpect_t,
std::initializer_list<U> il,
Args &&...args)
- : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, il, std::forward<Args>(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 <class E> struct expected_storage_base<void, E, false, false> {
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
nullptr>
constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
- : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, std::forward<Args>(args)...), m_has_val(false) {}
template <class U, class... Args,
detail::enable_if_t<std::is_constructible<
@@ -698,7 +701,7 @@ template <class E> struct expected_storage_base<void, E, false, false> {
constexpr explicit expected_storage_base(unexpect_t,
std::initializer_list<U> il,
Args &&...args)
- : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+ : m_unexpect(in_place, il, std::forward<Args>(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 <type_traits>
#include <vector>
+#include <array>
#include <string>
struct takes_init_and_variadic {
@@ -13,6 +14,27 @@ struct takes_init_and_variadic {
: v(l), t(std::forward<Args>(args)...) {}
};
+struct trivial_type {
+ int x;
+ int y;
+ trivial_type(int _x, int _y) : x{_x}, y{_y} {}
+ trivial_type(std::initializer_list<int> list) {
+ auto it = list.begin();
+ x = *it;
+ ++it;
+ y = *it;
+ }
+};
+
+struct takes_init_and_variadic_trivial {
+ trivial_type p;
+ std::tuple<int, int> t;
+ template <class... Args>
+ takes_init_and_variadic_trivial(std::initializer_list<int> l,
+ Args &&...args)
+ : p(l), t(std::forward<Args>(args)...) {}
+};
+
TEST_CASE("Constructors", "[constructors]") {
{
tl::expected<int,int> e;
@@ -38,6 +60,12 @@ TEST_CASE("Constructors", "[constructors]") {
REQUIRE(e == 42);
}
+ {
+ tl::expected<int, int> e(tl::in_place);
+ REQUIRE(e);
+ REQUIRE(e == 0);
+ }
+
{
tl::expected<std::vector<int>,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<int, std::tuple<int, int>> e(tl::unexpect, 0, 1);
+ REQUIRE(!e);
+ REQUIRE(std::get<0>(e.error()) == 0);
+ REQUIRE(std::get<1>(e.error()) == 1);
+ }
+
+ {
+ tl::expected<void, std::tuple<int, int>> e(tl::unexpect, 0, 1);
+ REQUIRE(!e);
+ REQUIRE(std::get<0>(e.error()) == 0);
+ REQUIRE(std::get<1>(e.error()) == 1);
+ }
+ {
+ tl::expected<void, std::vector<int>> e(tl::unexpect, 2, 1);
+ REQUIRE(!e);
+ REQUIRE(e.error()[0] == 1);
+ REQUIRE(e.error()[1] == 1);
+ }
+ {
+ tl::expected<std::vector<int>, std::vector<int>> e(tl::unexpect, 2, 1);
+ REQUIRE(!e);
+ REQUIRE(e.error()[0] == 1);
+ REQUIRE(e.error()[1] == 1);
+ }
+ {
+ tl::expected<std::vector<int>, 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<takes_init_and_variadic,int> 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<int, 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<int, takes_init_and_variadic_trivial> 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<void, takes_init_and_variadic_trivial> 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<int, std::vector<int>> e(tl::unexpect, 2, 1);
+ REQUIRE(!e);
+ REQUIRE(e.error()[0] == 1);
+ REQUIRE(e.error()[1] == 1);
+ }
+ {
+ tl::expected<std::vector<int>, trivial_type> e(tl::unexpect, 1, 2);
+ REQUIRE(!e);
+ REQUIRE(e.error().x == 1);
+ REQUIRE(e.error().y == 2);
+ }
+ {
+ tl::expected<std::vector<int>, 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<void, 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<int, int> e;
@@ -152,3 +267,11 @@ TEST_CASE("Constructors", "[constructors]") {
REQUIRE(e2.error().val == 's');
}
}
+
+TEST_CASE("Unexpected constructors", "[constructors]") {
+ REQUIRE(tl::unexpected<int>(1).error() == 1);
+ REQUIRE(tl::unexpected<int>(tl::in_place).error() == 0);
+ REQUIRE(tl::unexpected<int>(tl::in_place, 1).error() == 1);
+ REQUIRE(tl::unexpected<std::vector<int>>(tl::in_place, {1, 2, 3}).error() ==
+ std::vector<int>{1, 2, 3});
+}
--
2.44.0.windows.1

View File

@ -0,0 +1,106 @@
From d99fc8961341d494597a62f5513573660a7f076f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B8ger=20Hanseg=C3=A5rd?= <joger.hansegard@qt.io>
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 <QtCore/private/qglobal_p.h>
+#include <QtCore/qassert.h>
+#include <QtCore/qtconfigmacros.h>
+
#include <exception>
#include <functional>
#include <type_traits>
#include <utility>
+#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 <class T>
struct is_trivially_copy_constructible
@@ -91,11 +109,12 @@ template <class T, class A>
struct is_trivially_copy_constructible<std::vector<T, A>> : 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<T>
+ q23::detail::is_trivially_copy_constructible<T>
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
std::is_trivially_copy_assignable<T>
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
@@ -132,7 +151,8 @@ struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
#define TL_EXPECTED_11_CONSTEXPR constexpr
#endif
-namespace tl {
+QT_BEGIN_NAMESPACE
+namespace q23 {
template <class T, class E> 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 <class T> struct is_expected_impl : std::false_type {};
template <class T, class E>
struct is_expected_impl<expected<T, E>> : std::true_type {};
@@ -2474,6 +2494,7 @@ void swap(expected<T, E> &lhs,
expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
-} // namespace tl
+} // namespace q23
+QT_END_NAMESPACE
#endif
--
2.44.0.windows.1

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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."
}