From 008072cffd0cedcbe43df7a44c2058560ad0ad63 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 6 Dec 2023 22:02:42 +0100 Subject: [PATCH] QSpan: ensure interoperability with std::span We accepted QSpan as a NIH-type instead of waiting for C++20 and std::span, because we said that there's no impedance mismatch between the two, as they both implicitly convert into each other. But we actually never checked that they do. Fix this omission by adding constructors that treat std::span exactly the same as QSpan itself, and adding the respective static_assert()s to tst_QSpan to check that (within the constraints imposed by the standard on std::span), they actually do convert into each other. The only two problematic cases are that fixed-size std::span constructors are explicit, so span is only constructible, not convertible, from QSpan. Likewise, for an rvalue QSpan to be acceptable to the std::span constructor, QSpan needs to opt-in to enable_borrowed_range (while we're at it, do enable_view, too). We so far have rejected adding these opt-ins for our own container classes because we wanted to avoid the compile-time overhead of including the huge header into such central headers as those that define our containers. But std::span itself has to specialize these traits, and its range contructor has to use them, so they must be available from , too, possibly the stdlib puts the definition into a much smaller header. So just assume we can specialize it after including just , provided __cpp_lib_concepts is also defined. Change-Id: I2202869b60c98047256b0fbcb12336f5d8e550ba Reviewed-by: Thiago Macieira (cherry picked from commit 7f5b795f757ee62af71d8d47ccad19cbf681e0eb) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/tools/qspan_p.h | 44 ++++++++++++++++++++ tests/auto/corelib/tools/qspan/tst_qspan.cpp | 36 ++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/corelib/tools/qspan_p.h b/src/corelib/tools/qspan_p.h index f9de3ee4119..b3d6c13a708 100644 --- a/src/corelib/tools/qspan_p.h +++ b/src/corelib/tools/qspan_p.h @@ -37,6 +37,22 @@ namespace q20 { template class QSpan; +QT_BEGIN_INCLUDE_NAMESPACE +#ifdef __cpp_lib_span +#ifdef __cpp_lib_concepts +namespace std::ranges { +// Officially, these are defined in , but that is a heavy-hitter header. +// OTOH, must specialize these variable templates, too, so we assume that +// includes some meaningful subset of and just go ahead and use them: +template +constexpr inline bool enable_borrowed_range> = true; +template +constexpr inline bool enable_view> = true; +} // namespace std::ranges +#endif // __cpp_lib_concepts +#endif // __cpp_lib_span +QT_END_INCLUDE_NAMESPACE + namespace QSpanPrivate { template class QSpanBase; @@ -50,6 +66,15 @@ struct is_qspan_helper> : std::true_type {}; template using is_qspan = is_qspan_helper>; +template +struct is_std_span_helper : std::false_type {}; +#ifdef __cpp_lib_span +template +struct is_std_span_helper> : std::true_type {}; +#endif // __cpp_lib_span +template +using is_std_span = is_std_span_helper>; + template struct is_std_array_helper : std::false_type {}; template @@ -107,6 +132,7 @@ protected: using is_compatible_range = std::conjunction< // ### this needs more work, esp. extension to C++20 contiguous iterators std::negation>, + std::negation>, std::negation>, std::negation>>, is_compatible_range_helper @@ -197,6 +223,17 @@ public: : QSpanBase(other.data(), other.size()) {} +#ifdef __cpp_lib_span + template = true> + Q_IMPLICIT constexpr QSpanBase(std::span other) noexcept + : QSpanBase(other.data(), other.size()) + {} + + template = true> + Q_IMPLICIT constexpr QSpanBase(std::span other) + : QSpanBase(other.data(), other.size()) + {} +#endif // __cpp_lib_span }; // class QSpanBase (fixed extent) template @@ -247,6 +284,13 @@ public: Q_IMPLICIT constexpr QSpanBase(QSpan other) noexcept : QSpanBase(other.data(), other.size()) {} + +#if __cpp_lib_span + template = true> + Q_IMPLICIT constexpr QSpanBase(std::span other) noexcept + : QSpanBase(other.data(), other.size()) + {} +#endif // __cpp_lib_span }; // class QSpanBase (dynamic extent) } // namespace QSpanPrivate diff --git a/tests/auto/corelib/tools/qspan/tst_qspan.cpp b/tests/auto/corelib/tools/qspan/tst_qspan.cpp index 9eb3e611633..35539a7f425 100644 --- a/tests/auto/corelib/tools/qspan/tst_qspan.cpp +++ b/tests/auto/corelib/tools/qspan/tst_qspan.cpp @@ -8,6 +8,9 @@ #include #include +#ifdef __cpp_lib_span +#include +#endif #include namespace { @@ -49,6 +52,17 @@ static_assert(std::is_trivially_destructible_v>); static_assert(std::is_convertible_v, QSpan>); static_assert(std::is_convertible_v, QSpan>); +#ifdef __cpp_lib_span +static_assert(std::is_convertible_v, QSpan>); +static_assert(std::is_convertible_v, QSpan>); + +#ifdef __cpp_lib_concepts +// requires enable_borrowed_range +static_assert(std::is_convertible_v, std::span>); +static_assert(std::is_convertible_v, std::span>); +#endif // __cpp_lib_concepts +#endif // __cpp_lib_span + // // Mutable spans implicitly convert to read-only ones, but not vice versa: // @@ -60,6 +74,28 @@ static_assert(!std::is_convertible_v, QSpan>); static_assert(!std::is_convertible_v, QSpan>); static_assert(!std::is_convertible_v, QSpan>); +#ifdef __cpp_lib_span +static_assert(std::is_convertible_v, QSpan>); +static_assert(std::is_convertible_v, QSpan>); +static_assert(std::is_convertible_v, QSpan>); + +static_assert(!std::is_convertible_v, QSpan>); +static_assert(!std::is_convertible_v, QSpan>); +static_assert(!std::is_convertible_v, QSpan>); + +static_assert(std::is_convertible_v, std::span>); +// fixed-size std::span constructors are explicit: +static_assert(!std::is_convertible_v, std::span>); +static_assert(!std::is_convertible_v, std::span>); +// observe: is_convertible, but is_constuctible! +static_assert(std::is_constructible_v, QSpan>); +static_assert(std::is_constructible_v, QSpan>); + +static_assert(!std::is_convertible_v, std::span>); +static_assert(!std::is_convertible_v, std::span>); +static_assert(!std::is_convertible_v, std::span>); +#endif // __cpp_lib_span + class tst_QSpan : public QObject { Q_OBJECT