Long live QSpan as public API!
Provide qspan_p.h as backward-compatibility header. [ChangeLog][QtCore][QSpan] New Qt equivalent of std::span. Fixes: QTBUG-115022 Change-Id: I1cc27dc0aa1f7406f0a41d7a75f176cd7f858feb Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
5885db33e3
commit
03e78e5d62
@ -307,6 +307,7 @@ qt_internal_add_module(Core
|
||||
tools/qsharedpointer.cpp tools/qsharedpointer.h
|
||||
tools/qsharedpointer_impl.h
|
||||
tools/qsize.cpp tools/qsize.h
|
||||
tools/qspan.h
|
||||
tools/qspan_p.h
|
||||
tools/qstack.h
|
||||
tools/qtaggedpointer.h
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <qlist.h>
|
||||
#include "qlocale.h"
|
||||
#include "qlocale_p.h"
|
||||
#include "qspan.h"
|
||||
#include "qstringbuilder.h"
|
||||
#include "qstringmatcher.h"
|
||||
#include "qvarlengtharray.h"
|
||||
@ -30,7 +31,6 @@
|
||||
#endif
|
||||
|
||||
#include <private/qfunctions_p.h>
|
||||
#include <private/qspan_p.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
@ -12,6 +12,7 @@
|
||||
#endif
|
||||
|
||||
// std headers can unfortunately not be forward declared
|
||||
#include <cstddef> // std::size_t
|
||||
#include <utility>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -25,6 +26,7 @@ template <typename T1, typename T2>
|
||||
using QPair = std::pair<T1, T2>;
|
||||
template <typename T> class QQueue;
|
||||
template <typename T> class QSet;
|
||||
template <typename T, std::size_t E = std::size_t(-1) /* = std::dynamic_extent*/> class QSpan;
|
||||
template <typename T> class QStack;
|
||||
template <typename T, qsizetype Prealloc = 256> class QVarLengthArray;
|
||||
template <typename T> class QList;
|
||||
|
424
src/corelib/tools/qspan.h
Normal file
424
src/corelib/tools/qspan.h
Normal file
@ -0,0 +1,424 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QSPAN_H
|
||||
#define QSPAN_H
|
||||
|
||||
#include <QtCore/qcompilerdetection.h>
|
||||
#include <QtCore/qtypes.h>
|
||||
#include <QtCore/qcontainerfwd.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <QtCore/q20iterator.h>
|
||||
#include <QtCore/q20memory.h>
|
||||
#ifdef __cpp_lib_span
|
||||
#include <span>
|
||||
#endif
|
||||
#include <QtCore/q20type_traits.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// like std::dynamic_extent
|
||||
namespace q20 {
|
||||
inline constexpr auto dynamic_extent = std::size_t(-1);
|
||||
} // namespace q20
|
||||
|
||||
QT_BEGIN_INCLUDE_NAMESPACE
|
||||
#ifdef __cpp_lib_span
|
||||
#ifdef __cpp_lib_concepts
|
||||
namespace std::ranges {
|
||||
// Officially, these are defined in <ranges>, but that is a heavy-hitter header.
|
||||
// OTOH, <span> must specialize these variable templates, too, so we assume that
|
||||
// <span> includes some meaningful subset of <ranges> and just go ahead and use them:
|
||||
template <typename T, std::size_t E>
|
||||
constexpr inline bool enable_borrowed_range<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
|
||||
template <typename T, std::size_t E>
|
||||
constexpr inline bool enable_view<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
|
||||
} // namespace std::ranges
|
||||
#endif // __cpp_lib_concepts
|
||||
#endif // __cpp_lib_span
|
||||
QT_END_INCLUDE_NAMESPACE
|
||||
|
||||
namespace QSpanPrivate {
|
||||
|
||||
template <typename T, std::size_t E> class QSpanBase;
|
||||
|
||||
template <typename T>
|
||||
struct is_qspan_helper : std::false_type {};
|
||||
template <typename T, std::size_t E>
|
||||
struct is_qspan_helper<QSpan<T, E>> : std::true_type {};
|
||||
template <typename T, std::size_t E>
|
||||
struct is_qspan_helper<QSpanBase<T, E>> : std::true_type {};
|
||||
template <typename T>
|
||||
using is_qspan = is_qspan_helper<q20::remove_cvref_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
struct is_std_span_helper : std::false_type {};
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename T, std::size_t E>
|
||||
struct is_std_span_helper<std::span<T, E>> : std::true_type {};
|
||||
#endif // __cpp_lib_span
|
||||
template <typename T>
|
||||
using is_std_span = is_std_span_helper<q20::remove_cvref_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
struct is_std_array_helper : std::false_type {};
|
||||
template <typename T, std::size_t N>
|
||||
struct is_std_array_helper<std::array<T, N>> : std::true_type {};
|
||||
template <typename T>
|
||||
using is_std_array = is_std_array_helper<q20::remove_cvref_t<T>>;
|
||||
|
||||
template <typename From, typename To>
|
||||
using is_qualification_conversion =
|
||||
std::is_convertible<From(*)[], To(*)[]>; // https://eel.is/c++draft/span.cons#note-1
|
||||
template <typename From, typename To>
|
||||
constexpr inline bool is_qualification_conversion_v = is_qualification_conversion<From, To>::value;
|
||||
|
||||
// Replacements for std::ranges::XXX(), but only bringing in ADL XXX()s,
|
||||
// not doing the extra work C++20 requires
|
||||
template <typename Range>
|
||||
decltype(auto) adl_begin(Range &&r) { using std::begin; return begin(r); }
|
||||
template <typename Range>
|
||||
decltype(auto) adl_data(Range &&r) { using std::data; return data(r); }
|
||||
template <typename Range>
|
||||
decltype(auto) adl_size(Range &&r) { using std::size; return size(r); }
|
||||
|
||||
// Replacement for std::ranges::iterator_t (which depends on C++20 std::ranges::begin)
|
||||
// This one uses adl_begin() instead.
|
||||
template <typename Range>
|
||||
using iterator_t = decltype(QSpanPrivate::adl_begin(std::declval<Range&>()));
|
||||
template <typename Range>
|
||||
using range_reference_t = q20::iter_reference_t<QSpanPrivate::iterator_t<Range>>;
|
||||
|
||||
template <typename T>
|
||||
class QSpanCommon {
|
||||
protected:
|
||||
template <typename Iterator>
|
||||
using is_compatible_iterator = std::conjunction<
|
||||
std::is_base_of<
|
||||
std::random_access_iterator_tag,
|
||||
typename std::iterator_traits<Iterator>::iterator_category
|
||||
>,
|
||||
is_qualification_conversion<
|
||||
std::remove_reference_t<q20::iter_reference_t<Iterator>>,
|
||||
T
|
||||
>
|
||||
>;
|
||||
template <typename Iterator, typename End>
|
||||
using is_compatible_iterator_and_sentinel = std::conjunction<
|
||||
is_compatible_iterator<Iterator>,
|
||||
std::negation<std::is_convertible<End, std::size_t>>
|
||||
>;
|
||||
template <typename Range, typename = void> // wrap use of SFINAE-unfriendly iterator_t:
|
||||
struct is_compatible_range_helper : std::false_type {};
|
||||
template <typename Range>
|
||||
struct is_compatible_range_helper<Range, std::void_t<QSpanPrivate::iterator_t<Range>>>
|
||||
: is_compatible_iterator<QSpanPrivate::iterator_t<Range>> {};
|
||||
template <typename Range>
|
||||
using is_compatible_range = std::conjunction<
|
||||
// ### this needs more work, esp. extension to C++20 contiguous iterators
|
||||
std::negation<is_qspan<Range>>,
|
||||
std::negation<is_std_span<Range>>,
|
||||
std::negation<is_std_array<Range>>,
|
||||
std::negation<std::is_array<q20::remove_cvref_t<Range>>>,
|
||||
is_compatible_range_helper<Range>
|
||||
>;
|
||||
|
||||
// constraints
|
||||
template <typename Iterator>
|
||||
using if_compatible_iterator = std::enable_if_t<
|
||||
is_compatible_iterator<Iterator>::value
|
||||
, bool>;
|
||||
template <typename Iterator, typename End>
|
||||
using if_compatible_iterator_and_sentinel = std::enable_if_t<
|
||||
is_compatible_iterator_and_sentinel<Iterator, End>::value
|
||||
, bool>;
|
||||
template <typename Range>
|
||||
using if_compatible_range = std::enable_if_t<is_compatible_range<Range>::value, bool>;
|
||||
}; // class QSpanCommon
|
||||
|
||||
template <typename T, std::size_t E>
|
||||
class QSpanBase : protected QSpanCommon<T>
|
||||
{
|
||||
static_assert(E < size_t{(std::numeric_limits<qsizetype>::max)()},
|
||||
"QSpan only supports extents that fit into the signed size type (qsizetype).");
|
||||
|
||||
struct Enabled_t { explicit Enabled_t() = default; };
|
||||
static inline constexpr Enabled_t Enable{};
|
||||
|
||||
template <typename S, std::size_t N>
|
||||
using if_compatible_array = std::enable_if_t<
|
||||
N == E && is_qualification_conversion_v<S, T>
|
||||
, bool>;
|
||||
|
||||
template <typename S>
|
||||
using if_qualification_conversion = std::enable_if_t<
|
||||
is_qualification_conversion_v<S, T>
|
||||
, bool>;
|
||||
protected:
|
||||
using Base = QSpanCommon<T>;
|
||||
|
||||
// data members:
|
||||
T *m_data;
|
||||
static constexpr qsizetype m_size = qsizetype(E);
|
||||
|
||||
// types and constants:
|
||||
// (in QSpan only)
|
||||
|
||||
// constructors (need to be public d/t the way ctor inheriting works):
|
||||
public:
|
||||
template <std::size_t E2 = E, std::enable_if_t<E2 == 0, bool> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr} {}
|
||||
|
||||
template <typename It, typename Base::template if_compatible_iterator<It> = true>
|
||||
explicit constexpr QSpanBase(It first, qsizetype count)
|
||||
: m_data{q20::to_address(first)}
|
||||
{
|
||||
Q_ASSERT(count == m_size);
|
||||
}
|
||||
|
||||
template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
|
||||
explicit constexpr QSpanBase(It first, End last)
|
||||
: QSpanBase(first, last - first) {}
|
||||
|
||||
template <size_t N, std::enable_if_t<N == E, bool> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
|
||||
: QSpanBase(arr, N) {}
|
||||
|
||||
template <typename S, size_t N, if_compatible_array<S, N> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
|
||||
: QSpanBase(arr.data(), N) {}
|
||||
|
||||
template <typename S, size_t N, if_compatible_array<S, N> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
|
||||
: QSpanBase(arr.data(), N) {}
|
||||
|
||||
template <typename Range, typename Base::template if_compatible_range<Range> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(Range &&r)
|
||||
: QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
|
||||
qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
|
||||
{}
|
||||
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(QSpan<S, E> other) noexcept
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(QSpan<S> other)
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::span<S> other)
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
#endif // __cpp_lib_span
|
||||
}; // class QSpanBase (fixed extent)
|
||||
|
||||
template <typename T>
|
||||
class QSpanBase<T, q20::dynamic_extent> : protected QSpanCommon<T>
|
||||
{
|
||||
template <typename S>
|
||||
using if_qualification_conversion = std::enable_if_t<
|
||||
is_qualification_conversion_v<S, T>
|
||||
, bool>;
|
||||
protected:
|
||||
using Base = QSpanCommon<T>;
|
||||
|
||||
// data members:
|
||||
T *m_data;
|
||||
qsizetype m_size;
|
||||
|
||||
// constructors (need to be public d/t the way ctor inheriting works):
|
||||
public:
|
||||
Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr}, m_size{0} {}
|
||||
|
||||
template <typename It, typename Base::template if_compatible_iterator<It> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(It first, qsizetype count)
|
||||
: m_data{q20::to_address(first)}, m_size{count} {}
|
||||
|
||||
template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(It first, End last)
|
||||
: QSpanBase(first, last - first) {}
|
||||
|
||||
template <size_t N>
|
||||
Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
|
||||
: QSpanBase(arr, N) {}
|
||||
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
|
||||
: QSpanBase(arr.data(), N) {}
|
||||
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
|
||||
: QSpanBase(arr.data(), N) {}
|
||||
|
||||
template <typename Range, typename Base::template if_compatible_range<Range> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(Range &&r)
|
||||
: QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
|
||||
qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
|
||||
{}
|
||||
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(QSpan<S, N> other) noexcept
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
#if __cpp_lib_span
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::span<S, N> other) noexcept
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
#endif // __cpp_lib_span
|
||||
}; // class QSpanBase (dynamic extent)
|
||||
|
||||
} // namespace QSpanPrivate
|
||||
|
||||
template <typename T, std::size_t E>
|
||||
class QSpan
|
||||
#ifndef Q_QDOC
|
||||
: private QSpanPrivate::QSpanBase<T, E>
|
||||
#endif
|
||||
{
|
||||
using Base = QSpanPrivate::QSpanBase<T, E>;
|
||||
Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0,
|
||||
[[maybe_unused]] qsizetype n = 1) const
|
||||
{
|
||||
Q_ASSERT(pos >= 0);
|
||||
Q_ASSERT(pos <= size());
|
||||
Q_ASSERT(n >= 0);
|
||||
Q_ASSERT(n <= size() - pos);
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
static constexpr bool subspan_always_succeeds_v = N <= E && E != q20::dynamic_extent;
|
||||
public:
|
||||
// constants and types
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<element_type>;
|
||||
using size_type = qsizetype; // difference to std::span
|
||||
using difference_type = qptrdiff; // difference to std::span
|
||||
using pointer = element_type*;
|
||||
using const_pointer = const element_type*;
|
||||
using reference = element_type&;
|
||||
using const_reference = const element_type&;
|
||||
using iterator = pointer; // implementation-defined choice
|
||||
using const_iterator = const_pointer; // implementation-defined choice
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
static constexpr std::size_t extent = E;
|
||||
|
||||
// [span.cons], constructors, copy, and assignment
|
||||
using Base::Base;
|
||||
#ifdef Q_QDOC
|
||||
template <typename It> using if_compatible_iterator = bool;
|
||||
template <typename S> using if_qualification_conversion = bool;
|
||||
template <typename Range> using if_compatible_range = bool;
|
||||
template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, qsizetype count);
|
||||
template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, It last);
|
||||
template <size_t N> constexpr QSpan(q20::type_identity_t<T> (&arr)[N]) noexcept;
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::array<S, N> &arr) noexcept;
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(const std::array<S, N> &arr) noexcept;
|
||||
template <typename Range, if_compatible_range<Range> = true> constexpr QSpan(Range &&r);
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(QSpan<S, N> other) noexcept;
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::span<S, N> other) noexcept;
|
||||
#endif // Q_QDOC
|
||||
|
||||
// [span.obs]
|
||||
[[nodiscard]] constexpr size_type size() const noexcept { return this->m_size; }
|
||||
[[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(T); }
|
||||
[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
// [span.elem]
|
||||
[[nodiscard]] constexpr reference operator[](size_type idx) const
|
||||
{ verify(idx); return data()[idx]; }
|
||||
[[nodiscard]] constexpr reference front() const { verify(); return *data(); }
|
||||
[[nodiscard]] constexpr reference back() const { verify(); return data()[size() - 1]; }
|
||||
[[nodiscard]] constexpr pointer data() const noexcept { return this->m_data; }
|
||||
|
||||
// [span.iterators]
|
||||
[[nodiscard]] constexpr iterator begin() const noexcept { return data(); }
|
||||
[[nodiscard]] constexpr iterator end() const noexcept { return data() + size(); }
|
||||
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
|
||||
[[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
|
||||
[[nodiscard]] constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
|
||||
[[nodiscard]] constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
|
||||
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
|
||||
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
|
||||
|
||||
// [span.sub]
|
||||
template <std::size_t Count>
|
||||
[[nodiscard]] constexpr QSpan<T, Count> first() const
|
||||
noexcept(subspan_always_succeeds_v<Count>)
|
||||
{
|
||||
static_assert(Count <= E,
|
||||
"Count cannot be larger than the span's extent.");
|
||||
verify(0, Count);
|
||||
return QSpan<T, Count>{data(), Count};
|
||||
}
|
||||
|
||||
template <std::size_t Count>
|
||||
[[nodiscard]] constexpr QSpan<T, Count> last() const
|
||||
noexcept(subspan_always_succeeds_v<Count>)
|
||||
{
|
||||
static_assert(Count <= E,
|
||||
"Count cannot be larger than the span's extent.");
|
||||
verify(0, Count);
|
||||
return QSpan<T, Count>{data() + (size() - Count), Count};
|
||||
}
|
||||
|
||||
template <std::size_t Offset>
|
||||
[[nodiscard]] constexpr auto subspan() const
|
||||
noexcept(subspan_always_succeeds_v<Offset>)
|
||||
{
|
||||
static_assert(Offset <= E,
|
||||
"Offset cannot be larger than the span's extent.");
|
||||
verify(Offset, 0);
|
||||
if constexpr (E == q20::dynamic_extent)
|
||||
return QSpan<T>{data() + Offset, qsizetype(size() - Offset)};
|
||||
else
|
||||
return QSpan<T, E - Offset>{data() + Offset, qsizetype(E - Offset)};
|
||||
}
|
||||
|
||||
template <std::size_t Offset, std::size_t Count>
|
||||
[[nodiscard]] constexpr auto subspan() const
|
||||
noexcept(subspan_always_succeeds_v<Offset + Count>)
|
||||
{ return subspan<Offset>().template first<Count>(); }
|
||||
|
||||
[[nodiscard]] constexpr QSpan<T> first(size_type n) const { verify(0, n); return {data(), n}; }
|
||||
[[nodiscard]] constexpr QSpan<T> last(size_type n) const { verify(0, n); return {data() + (size() - n), n}; }
|
||||
[[nodiscard]] constexpr QSpan<T> subspan(size_type pos) const { verify(pos, 0); return {data() + pos, size() - pos}; }
|
||||
[[nodiscard]] constexpr QSpan<T> subspan(size_type pos, size_type n) const { return subspan(pos).first(n); }
|
||||
|
||||
// Qt-compatibility API:
|
||||
[[nodiscard]] bool isEmpty() const noexcept { return empty(); }
|
||||
// nullary first()/last() clash with first<>() and last<>(), so they're not provided for QSpan
|
||||
[[nodiscard]] constexpr QSpan<T> sliced(size_type pos) const { return subspan(pos); }
|
||||
[[nodiscard]] constexpr QSpan<T> sliced(size_type pos, size_type n) const { return subspan(pos, n); }
|
||||
|
||||
}; // class QSpan
|
||||
|
||||
// [span.deduct]
|
||||
template <class It, class EndOrSize>
|
||||
QSpan(It, EndOrSize) -> QSpan<std::remove_reference_t<q20::iter_reference_t<It>>>;
|
||||
template <class T, std::size_t N>
|
||||
QSpan(T (&)[N]) -> QSpan<T, N>;
|
||||
template <class T, std::size_t N>
|
||||
QSpan(std::array<T, N> &) -> QSpan<T, N>;
|
||||
template <class T, std::size_t N>
|
||||
QSpan(const std::array<T, N> &) -> QSpan<const T, N>;
|
||||
template <class R>
|
||||
QSpan(R&&) -> QSpan<std::remove_reference_t<QSpanPrivate::range_reference_t<R>>>;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSPAN_H
|
639
src/corelib/tools/qspan.qdoc
Normal file
639
src/corelib/tools/qspan.qdoc
Normal file
@ -0,0 +1,639 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
/*!
|
||||
\class QSpan
|
||||
\inmodule QtCore
|
||||
\since 6.7
|
||||
\brief A non-owning container over contiguous data.
|
||||
\ingroup tools
|
||||
\reentrant
|
||||
|
||||
A QSpan references a contiguous portion of another contiguous container.
|
||||
It acts as an interface type for all kinds of contiguous containers,
|
||||
without the need to construct an owning container such as QList or
|
||||
std::vector first.
|
||||
|
||||
The data referenced by a QSpan may be represented as an array (or
|
||||
array-compatible data-structure such as QList, std::vector,
|
||||
QVarLengthArray, etc.). QSpan itself merely stores a pointer to the data,
|
||||
so users must ensure that QSpan objects do not outlive the data they
|
||||
reference.
|
||||
|
||||
Unlike views such as QStringView, QLatin1StringView and QUtf8StringView,
|
||||
referenced data can be modified through a QSpan object. To prevent this,
|
||||
construct a QSpan over a \c{const T}:
|
||||
|
||||
\code
|
||||
int numbers[] = {0, 1, 2};
|
||||
QSpan<int> span = numbers;
|
||||
span[0] = 42;
|
||||
// numbers == {42, 1, 2};
|
||||
QSpan<const int> cspan = numbers;
|
||||
cspan[0] = 0; // ERROR: cspan[0] is read-only
|
||||
\endcode
|
||||
|
||||
A QSpan can be \e{fixed-size} or \e{variable-sized}.
|
||||
|
||||
A variable-sized span is formed by omitting the second template argument
|
||||
(or setting it to \c{std::dynamic_extent}, which is, however, only
|
||||
available in C++20 builds), as seen in the example above.
|
||||
|
||||
A fixed-size span is formed by passing a number as the second template
|
||||
argument:
|
||||
|
||||
\code
|
||||
int numbers[] = {0, 1, 2};
|
||||
QSpan<int, 3> span = numbers;
|
||||
QSpan<const int, 3> = numbers; // also OK
|
||||
\endcode
|
||||
|
||||
As the name suggests, a fixed-size span's size() is fixed at compile-time
|
||||
whereas the size() of a variable-sized span is determined only at run-time.
|
||||
|
||||
A fixed-size span is not default-constructible (unless its \l extent is zero
|
||||
(0)). A variable-sized span \e{is} default-constructible and will have
|
||||
\c{data() == nullptr} and \c{size() == 0}.
|
||||
|
||||
A fixed-size span can be implicitly converted into a variable-sized one.
|
||||
The opposite direction (variable-length into fixed-length) has the
|
||||
precondition that both span's sizes must match.
|
||||
|
||||
Unlike with owning containers, \c{const} is \e{shallow} in QSpan: you can
|
||||
still modify the data through a const QSpan (but not through a
|
||||
\c{QSpan<const T>}), and begin() and end() are not overloaded on
|
||||
\c{const}/non-\c{const}. There are cbegin() and cend(), though, that return
|
||||
const_iterators which prevent modification of the data even though \c{T} is
|
||||
not const:
|
||||
\code
|
||||
int numbers[] = {0, 1, 2};
|
||||
const QSpan<int> span = numbers;
|
||||
span.front() = 42; // OK, numbers[0] == 42 now
|
||||
*span.begin() = 31; // OK, numbers[0] == 31 now
|
||||
*span.cbegin() = -1; // ERROR: cannot assign through a const_iterator
|
||||
\endcode
|
||||
|
||||
QSpan should be passed by value, not by reference-to-const:
|
||||
|
||||
\code
|
||||
void consume(QSpan<const int> data); // OK
|
||||
void consume(const QSpan<const int> &data); // works, but is non-idiomatic and less efficient
|
||||
\endcode
|
||||
|
||||
\c{QSpan<T,N>} is a \e{Literal Type}, regardless of whether \c{T} is a
|
||||
Literal Type or not.
|
||||
|
||||
\section2 QSpan vs. std::span
|
||||
\target span-STL
|
||||
|
||||
QSpan is closely modelled after
|
||||
\l{https://en.cppreference.com/w/cpp/container/span}{std::span}, but has a
|
||||
few differences which we'll discuss here. Since they both implicitly
|
||||
convert into each other, you're free to choose whichever one you like best
|
||||
in your own code.
|
||||
|
||||
\list
|
||||
\li QSpan is using the signed qsizetype as \c{size_type}
|
||||
whereas \c{std::span} uses \c{size_t}.
|
||||
\li All QSpan constructors are implicit;
|
||||
many \c{std::span} ones are \c{explicit}.
|
||||
\li QSpan can be constructed from rvalue owning containers, \c{std::span} can not.
|
||||
\endlist
|
||||
|
||||
The last two are required for source-compatibility when functions that took
|
||||
owning containers are converted to take QSpan instead, which is a
|
||||
vitally-important use-case in Qt. The use of qsizetype is for consistency
|
||||
with the rest of Qt containers. QSpan template arguments still use size_t
|
||||
to avoid introducing unnecessary error conditions (negative sizes).
|
||||
|
||||
\section2 Compatible Iterators
|
||||
\target span-compatible-iterators
|
||||
|
||||
QSpan can be constructed from an iterator and size or from an
|
||||
iterator pair, provided the iterators are \e{compatible} ones.
|
||||
Eventually, this should mean C++20 \c{std::contiguous_iterator} and
|
||||
\c{std::sentinel_for}, but while Qt still supports C++17, only raw pointers
|
||||
are considered contiguous iterators.
|
||||
|
||||
\section2 Compatible Ranges
|
||||
\target span-compatible-ranges
|
||||
|
||||
QSpan can also be constructed from a \e{compatible} range. A range is
|
||||
compatible if it has \l{span-compatible-iterators}{compatible iterators}.
|
||||
|
||||
\sa QList, QStringView, QLatin1StringView, QUtf8StringView
|
||||
*/
|
||||
|
||||
//
|
||||
// Nested types and constants
|
||||
//
|
||||
|
||||
/*!
|
||||
\typedef QSpan::element_type
|
||||
|
||||
An alias for \c{T}. Includes the \c{const}, if any.
|
||||
|
||||
This alias is provided for compatbility with the STL.
|
||||
|
||||
\sa value_type, pointer
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::value_type
|
||||
|
||||
An alias for \c{T}. Excludes the \c{const}, if any.
|
||||
|
||||
This alias is provided for compatbility with the STL.
|
||||
|
||||
\sa element_type
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::size_type
|
||||
|
||||
An alias for qsizetype. This \l{span-STL}{differs from \c{std::span}}.
|
||||
|
||||
This alias is provided for compatbility with the STL.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::difference_type
|
||||
|
||||
An alias for qptrdiff. This \l{span-STL}{differs from \c{std::span}}.
|
||||
|
||||
This alias is provided for compatbility with the STL.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::pointer
|
||||
|
||||
An alias for \c{T*} and \c{element_type*}, respectively. Includes the \c{const}, if any.
|
||||
|
||||
This alias is provided for compatbility with the STL.
|
||||
|
||||
\sa element_type, const_pointer, reference, iterator
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::const_pointer
|
||||
|
||||
An alias for \c{const T*} and \c{const element_type*}, respectively.
|
||||
|
||||
This alias is provided for compatbility with the STL.
|
||||
|
||||
\sa element_type, pointer, const_reference, const_iterator
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::reference
|
||||
|
||||
An alias for \c{T&} and \c{element_type&}, respectively. Includes the \c{const}, if any.
|
||||
|
||||
This alias is provided for compatbility with the STL.
|
||||
|
||||
\sa element_type, const_reference, pointer
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::const_reference
|
||||
|
||||
An alias for \c{const T&} and \c{const element_type&}, respectively.
|
||||
|
||||
This alias is provided for compatbility with the STL.
|
||||
|
||||
\sa element_type, reference, const_pointer
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::iterator
|
||||
|
||||
An alias for \c{T*} and \c{pointer}, respectively. Includes the \c{const}, if any.
|
||||
|
||||
\sa pointer, const_iterator, reverse_iterator
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::const_iterator
|
||||
|
||||
An alias for \c{const T*} and \c{const_pointer}, respectively.
|
||||
|
||||
\sa const_pointer, iterator, const_reverse_iterator
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::reverse_iterator
|
||||
|
||||
An alias for \c{std::reverse_iterator<iterator>}. Includes the \c{const}, if any.
|
||||
|
||||
\sa iterator, const_reverse_iterator
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QSpan::const_reverse_iterator
|
||||
|
||||
An alias for \c{std::reverse_iterator<const_iterator>}.
|
||||
|
||||
\sa const_iterator, reverse_iterator
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable QSpan::extent
|
||||
|
||||
The second template argument of \c{QSpan<T, E>}, that is, \c{E}. This is
|
||||
\c{std::dynamic_extent} for variable-sized spans.
|
||||
|
||||
\note While all other sizes and indexes in QSpan use qsizetype, this
|
||||
variable, like \c{E}, is actually of type \c{size_t}, for compatibility with
|
||||
\c{std::span} and \c{std::dynamic_extent}.
|
||||
|
||||
\sa size()
|
||||
*/
|
||||
|
||||
//
|
||||
// Constructors and SMFs
|
||||
//
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T,E>::QSpan()
|
||||
|
||||
Default constructor.
|
||||
|
||||
This constructor is only present if \c{E} is either zero (0) or
|
||||
\c{std::dynamic_extent}. In other words: only fixed-zero-sized or variable-sized spans
|
||||
are default-constructible.
|
||||
|
||||
\sa extent
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T,E>::QSpan(const QSpan &other)
|
||||
\fn template <typename T, size_t E> QSpan<T,E>::QSpan(QSpan &&other)
|
||||
\fn template <typename T, size_t E> QSpan<T,E> &QSpan<T,E>::operator=(const QSpan &other)
|
||||
\fn template <typename T, size_t E> QSpan<T,E> &QSpan<T,E>::operator=(QSpan &&other)
|
||||
\fn template <typename T, size_t E> QSpan<T,E>::~QSpan()
|
||||
|
||||
These Special Member Functions are implicitly-defined.
|
||||
|
||||
\note Moves are equivalent to copies. Only data() and size() are copied
|
||||
from span to span, not the referenced data.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <typename It, if_compatible_iterator<It>> QSpan<T,E>::QSpan(It first, qsizetype count)
|
||||
|
||||
Constructs a QSpan referencing the data starting at \a first and having length
|
||||
\a count.
|
||||
|
||||
\c{[first, count)} must be a valid range.
|
||||
|
||||
\note This constructor participates in overload resolution only if \c{It}
|
||||
is \l{span-compatible-iterators}{a compatible iterator}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <typename It, if_compatible_iterator<It>> QSpan<T,E>::QSpan(It first, It last)
|
||||
|
||||
Constructs a QSpan referencing the data starting at \a first and having length
|
||||
(\a last - \a first).
|
||||
|
||||
\c{[first, last)} must be a valid range.
|
||||
|
||||
\note This constructor participates in overload resolution only if \c{It}
|
||||
is \l{span-compatible-iterators}{a compatible iterator}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <size_t N> QSpan<T,E>::QSpan(q20::type_identity_t<T> (&arr)[N]);
|
||||
\fn template <typename T, size_t E> template <typename S, size_t N, if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(std::array<S, N> &arr);
|
||||
\fn template <typename T, size_t E> template <typename S, size_t N, if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(const std::array<S, N> &arr);
|
||||
|
||||
Constructs a QSpan referencing the data in the supplied array \a arr.
|
||||
|
||||
\note This constructor participates in overload resolution only if
|
||||
\list
|
||||
\li either \c{N} or \l{extent} are \c{std::dynamic_extent} or otherwise \l{extent} \c{==} \c{N}
|
||||
\li and either \c{S} or \c{const S} are the same as \c{T}.
|
||||
\endlist
|
||||
|
||||
\note \c{q20::type_identity_t} is a C++17 backport of C++20's
|
||||
\l{https://en.cppreference.com/w/cpp/types/type_identity}{\c{std::type_identity_t}}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <typename Range, if_compatible_range<Range> = true> QSpan<T,E>::QSpan(Range &&r)
|
||||
|
||||
Constructs a QSpan referencing the data in the supplied range \a r.
|
||||
|
||||
\note This constructor participates in overload resolution only if \c{Range}
|
||||
is \l{span-compatible-ranges}{a compatible range}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <typename S, size_t N, if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(QSpan<S, N> other);
|
||||
\fn template <typename T, size_t E> template <typename S, size_t N, if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(std::span<S, N> other);
|
||||
|
||||
Constructs a QSpan referencing the data in the supplied span \a other.
|
||||
|
||||
\note This constructor participates in overload resolution only if
|
||||
\list
|
||||
\li either \c{N} or \l{extent} are \c{std::dynamic_extent} or \l{extent} \c{==} \c{N}
|
||||
\li and either \c{S} or \c{const S} are the same as \c{T}.
|
||||
\endlist
|
||||
*/
|
||||
|
||||
//
|
||||
// Member functions: sizes
|
||||
//
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::size() const
|
||||
|
||||
Returns the size of the span, that is, the number of elements it references.
|
||||
|
||||
\sa size_bytes(), empty(), isEmpty()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::size_bytes() const
|
||||
|
||||
Returns the size of the span in bytes, that is, the number of elements
|
||||
multiplied by \c{sizeof(T)}.
|
||||
|
||||
\sa size(), empty(), isEmpty()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::empty() const
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::isEmpty() const
|
||||
|
||||
Returns whether the span is empty, that is, whether \c{size() == 0}.
|
||||
|
||||
These functions do the same thing: empty() is provided for STL
|
||||
compatibility and isEmpty() is provided for Qt compatibility.
|
||||
|
||||
\sa size(), size_bytes()
|
||||
*/
|
||||
|
||||
//
|
||||
// element access
|
||||
//
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::operator[](size_type idx) const
|
||||
|
||||
Returns a reference to the element at index \a idx in the span.
|
||||
|
||||
The index must be in range, that is, \a idx >= 0 and \a idx < size(),
|
||||
otherwise the behavior is undefined.
|
||||
|
||||
\sa front(), back(), size(), empty()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::front() const
|
||||
|
||||
Returns a reference to the first element in the span.
|
||||
|
||||
The span must not be empty, otherwise the behavior is undefined.
|
||||
|
||||
\sa operator[](), back(), size(), empty()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::back() const
|
||||
|
||||
Returns a reference to the last element in the span.
|
||||
|
||||
The span must not be empty, otherwise the behavior is undefined.
|
||||
|
||||
\sa operator[](), front(), size(), empty()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::data() const
|
||||
|
||||
Returns a pointer to the beginning of the span.
|
||||
|
||||
The same as calling begin().
|
||||
|
||||
\sa begin(), front()
|
||||
*/
|
||||
|
||||
//
|
||||
// iterators
|
||||
//
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::begin() const
|
||||
|
||||
Returns an interator pointing at the beginning of the span.
|
||||
|
||||
Because QSpan iterators are just pointers, this is the same as calling
|
||||
data().
|
||||
|
||||
\sa end(), cbegin(), rbegin(), crbegin(), data()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::end() const
|
||||
|
||||
Returns an iterator pointing to one past the end of the span.
|
||||
|
||||
Because QSpan iterators are just pointers, this it the same as calling
|
||||
\c{data() + size()}.
|
||||
|
||||
\sa begin(), cend(), rend(), crend(), data(), size()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::cbegin() const
|
||||
|
||||
Returns a const_iterator pointing to the beginning of the span.
|
||||
|
||||
This will return a read-only iterator even if \c{T} is not \c{const}:
|
||||
\code
|
||||
QSpan<int> span = ~~~;
|
||||
*span.begin() = 42; // OK
|
||||
*span.cbegin() = 42; // ERROR: cannot assign through a const_iterator
|
||||
\endcode
|
||||
|
||||
\sa cend(), begin(), crbegin(), rbegin(), data()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::cend() const
|
||||
|
||||
Returns a const_iterator pointing to one past the end of the span.
|
||||
|
||||
\sa cbegin(), end(), crend(), rend(), data(), size()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::rbegin() const
|
||||
|
||||
Returns a reverse_iterator pointing to the beginning of the reversed span.
|
||||
|
||||
\sa rend(), crbegin(), begin(), cbegin()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::rend() const
|
||||
|
||||
Returns a reverse_iterator pointing to one past the end of the reversed span.
|
||||
|
||||
\sa rbegin(), crend(), end(), cend()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::crbegin() const
|
||||
|
||||
Returns a const_reverse_iterator pointing to the beginning of the reversed span.
|
||||
|
||||
\sa crend(), rbegin(), cbegin(), begin()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::crend() const
|
||||
|
||||
Returns a const_reverse_iterator pointing to one past the end of the reversed span.
|
||||
|
||||
\sa crbegin(), rend(), cend(), end()
|
||||
*/
|
||||
|
||||
//
|
||||
// compile-time subspans:
|
||||
//
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <size_t Count> QSpan<T, E>::first() const
|
||||
\keyword first-t
|
||||
|
||||
Returns a fixed-sized span of size \c{Count} referencing the first \c{Count} elements of \c{*this}.
|
||||
|
||||
The span must hold at least \c{Count} elements (\c{E} >= \c{Count} \e{and}
|
||||
size() >= \c{Count}), otherwise the behavior is undefined.
|
||||
|
||||
\sa first(QSpan::size_type), last(), subspan()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <size_t Count> QSpan<T, E>::last() const
|
||||
\keyword last-t
|
||||
|
||||
Returns a fixed-sized span of size \c{Count} referencing the last \c{Count} elements of \c{*this}.
|
||||
|
||||
The span must hold at least \c{Count} elements (\c{E} >= \c{Count} \e{and}
|
||||
size() >= \c{Count}), otherwise the behavior is undefined.
|
||||
|
||||
\sa last(QSpan::size_type), first(), subspan()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <size_t Offset> QSpan<T, E>::subspan() const
|
||||
\keyword subspan-t1
|
||||
|
||||
Returns a span of size \c{E - Offset} referencing the remainder of this span
|
||||
after dropping the first \c{Offset} elements.
|
||||
|
||||
If \c{*this} is a variable-sized span, the return type is a variable-sized
|
||||
span, otherwise it is a fixed-sized span.
|
||||
|
||||
This span must hold at least \c{Offset} elements (\c{E} >= \c{Offset} \e{and}
|
||||
size() >= \c{Offset}), otherwise the behavior is undefined.
|
||||
|
||||
\sa subspan(QSpan::size_type), subspan(), first(), last()
|
||||
*/
|
||||
|
||||
#if 0 // needs fix for QTBUG-118080 integrated into qt5.git
|
||||
/*!
|
||||
\fn template <typename T, size_t E> template <size_t Offset, size_t Count> QSpan<T, E>::subspan() const
|
||||
\keyword subspan-t2
|
||||
|
||||
Returns a span of size \c{Count} referencing the \c{Count} elements of this
|
||||
span starting at \c{Offset}.
|
||||
|
||||
If \c{*this} is a variable-sized span, the return type is a variable-sized
|
||||
span, otherwise it is a fixed-sized span.
|
||||
|
||||
This span must hold at least \c{Offset + Count} elements (\c{E} >=
|
||||
\c{Offset + Count} \e{and} size() >= \c{Offset + Count}), otherwise the
|
||||
behavior is undefined.
|
||||
|
||||
\sa subspan(QSpan::size_type, QSpan::size_type), subspan(), first(), last()
|
||||
*/
|
||||
#endif
|
||||
|
||||
//
|
||||
// runtime subspans:
|
||||
//
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::first(qsizetype n) const
|
||||
\keyword first-n
|
||||
|
||||
Returns a variable-sized span of size \a n referencing the first \a n elements of \c{*this}.
|
||||
|
||||
\a n must be non-negative.
|
||||
|
||||
The span must hold at least \a n elements (\c{E} >= \a n \e{and} size() >=
|
||||
\a n), otherwise the behavior is undefined.
|
||||
|
||||
\sa {first-t}{first<N>()}, last(QSpan::size_type), subspan(QSpan::size_type),
|
||||
subspan(QSpan::size_type, QSpan::size_type)
|
||||
\sa sliced()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::last(qsizetype n) const
|
||||
\keyword last-n
|
||||
|
||||
Returns a variable-sized span of size \a n referencing the last \a n elements of \c{*this}.
|
||||
|
||||
\a n must be non-negative.
|
||||
|
||||
The span must hold at least \a n elements (\c{E} >= \a n \e{and}
|
||||
size() >= \a n), otherwise the behavior is undefined.
|
||||
|
||||
\sa last(), first(QSpan::size_type), subspan(QSpan::size_type),
|
||||
subspan(QSpan::size_type, QSpan::size_type), sliced()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::subspan(qsizetype pos) const
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::sliced(qsizetype pos) const
|
||||
\keyword subspan-n1
|
||||
|
||||
Returns a variable-sized span of size \c{size() - pos} referencing the
|
||||
remainder of this span after dropping the first \a pos elements.
|
||||
|
||||
\a pos must be non-negative.
|
||||
|
||||
This span must hold at least \a pos elements (\c{E} >= \a pos \e{and}
|
||||
size() >= \a pos), otherwise the behavior is undefined.
|
||||
|
||||
These functions do the same thing: subspan() is provided for STL
|
||||
compatibility and sliced() is provided for Qt compatibility.
|
||||
|
||||
\sa subspan(), first(QSpan::size_type), last(QSpan::size_type)
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::subspan(qsizetype pos, qsizetype n) const
|
||||
\fn template <typename T, size_t E> QSpan<T, E>::sliced(qsizetype pos, qsizetype n) const
|
||||
\keyword subspan-n2
|
||||
|
||||
Returns a variable-sized span of size \a n referencing the \a n elements of
|
||||
this span starting at \a pos.
|
||||
|
||||
Both \a pos and \a n must be non-negative.
|
||||
|
||||
This span must hold at least \c{pos + n} elements (\c{E} >=
|
||||
\c{pos + n} \e{and} size() >= \c{pos + n}), otherwise the
|
||||
behavior is undefined.
|
||||
|
||||
These functions do the same thing: subspan() is provided for STL
|
||||
compatibility and sliced() is provided for Qt compatibility.
|
||||
|
||||
\sa subspan(), first(QSpan::size_type), last(QSpan::size_type)
|
||||
*/
|
||||
|
@ -4,6 +4,8 @@
|
||||
#ifndef QSPAN_P_H
|
||||
#define QSPAN_P_H
|
||||
|
||||
#include <QtCore/qspan.h>
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
@ -15,409 +17,8 @@
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/qcompilerdetection.h>
|
||||
#include <QtCore/qtypes.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <QtCore/q20iterator.h>
|
||||
#include <QtCore/q20memory.h>
|
||||
#ifdef __cpp_lib_span
|
||||
#include <span>
|
||||
#endif
|
||||
#include <QtCore/q20type_traits.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// like std::dynamic_extent
|
||||
namespace q20 {
|
||||
inline constexpr auto dynamic_extent = std::size_t(-1);
|
||||
} // namespace q20
|
||||
|
||||
template <typename T, std::size_t E = q20::dynamic_extent> class QSpan;
|
||||
|
||||
QT_BEGIN_INCLUDE_NAMESPACE
|
||||
#ifdef __cpp_lib_span
|
||||
#ifdef __cpp_lib_concepts
|
||||
namespace std::ranges {
|
||||
// Officially, these are defined in <ranges>, but that is a heavy-hitter header.
|
||||
// OTOH, <span> must specialize these variable templates, too, so we assume that
|
||||
// <span> includes some meaningful subset of <ranges> and just go ahead and use them:
|
||||
template <typename T, std::size_t E>
|
||||
constexpr inline bool enable_borrowed_range<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
|
||||
template <typename T, std::size_t E>
|
||||
constexpr inline bool enable_view<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
|
||||
} // namespace std::ranges
|
||||
#endif // __cpp_lib_concepts
|
||||
#endif // __cpp_lib_span
|
||||
QT_END_INCLUDE_NAMESPACE
|
||||
|
||||
namespace QSpanPrivate {
|
||||
|
||||
template <typename T, std::size_t E> class QSpanBase;
|
||||
|
||||
template <typename T>
|
||||
struct is_qspan_helper : std::false_type {};
|
||||
template <typename T, std::size_t E>
|
||||
struct is_qspan_helper<QSpan<T, E>> : std::true_type {};
|
||||
template <typename T, std::size_t E>
|
||||
struct is_qspan_helper<QSpanBase<T, E>> : std::true_type {};
|
||||
template <typename T>
|
||||
using is_qspan = is_qspan_helper<q20::remove_cvref_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
struct is_std_span_helper : std::false_type {};
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename T, std::size_t E>
|
||||
struct is_std_span_helper<std::span<T, E>> : std::true_type {};
|
||||
#endif // __cpp_lib_span
|
||||
template <typename T>
|
||||
using is_std_span = is_std_span_helper<q20::remove_cvref_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
struct is_std_array_helper : std::false_type {};
|
||||
template <typename T, std::size_t N>
|
||||
struct is_std_array_helper<std::array<T, N>> : std::true_type {};
|
||||
template <typename T>
|
||||
using is_std_array = is_std_array_helper<q20::remove_cvref_t<T>>;
|
||||
|
||||
template <typename From, typename To>
|
||||
using is_qualification_conversion =
|
||||
std::is_convertible<From(*)[], To(*)[]>; // https://eel.is/c++draft/span.cons#note-1
|
||||
template <typename From, typename To>
|
||||
constexpr inline bool is_qualification_conversion_v = is_qualification_conversion<From, To>::value;
|
||||
|
||||
// Replacements for std::ranges::XXX(), but only bringing in ADL XXX()s,
|
||||
// not doing the extra work C++20 requires
|
||||
template <typename Range>
|
||||
decltype(auto) adl_begin(Range &&r) { using std::begin; return begin(r); }
|
||||
template <typename Range>
|
||||
decltype(auto) adl_data(Range &&r) { using std::data; return data(r); }
|
||||
template <typename Range>
|
||||
decltype(auto) adl_size(Range &&r) { using std::size; return size(r); }
|
||||
|
||||
// Replacement for std::ranges::iterator_t (which depends on C++20 std::ranges::begin)
|
||||
// This one uses adl_begin() instead.
|
||||
template <typename Range>
|
||||
using iterator_t = decltype(QSpanPrivate::adl_begin(std::declval<Range&>()));
|
||||
template <typename Range>
|
||||
using range_reference_t = q20::iter_reference_t<QSpanPrivate::iterator_t<Range>>;
|
||||
|
||||
template <typename T>
|
||||
class QSpanCommon {
|
||||
protected:
|
||||
template <typename Iterator>
|
||||
using is_compatible_iterator = std::conjunction<
|
||||
std::is_base_of<
|
||||
std::random_access_iterator_tag,
|
||||
typename std::iterator_traits<Iterator>::iterator_category
|
||||
>,
|
||||
is_qualification_conversion<
|
||||
std::remove_reference_t<q20::iter_reference_t<Iterator>>,
|
||||
T
|
||||
>
|
||||
>;
|
||||
template <typename Iterator, typename End>
|
||||
using is_compatible_iterator_and_sentinel = std::conjunction<
|
||||
is_compatible_iterator<Iterator>,
|
||||
std::negation<std::is_convertible<End, std::size_t>>
|
||||
>;
|
||||
template <typename Range, typename = void> // wrap use of SFINAE-unfriendly iterator_t:
|
||||
struct is_compatible_range_helper : std::false_type {};
|
||||
template <typename Range>
|
||||
struct is_compatible_range_helper<Range, std::void_t<QSpanPrivate::iterator_t<Range>>>
|
||||
: is_compatible_iterator<QSpanPrivate::iterator_t<Range>> {};
|
||||
template <typename Range>
|
||||
using is_compatible_range = std::conjunction<
|
||||
// ### this needs more work, esp. extension to C++20 contiguous iterators
|
||||
std::negation<is_qspan<Range>>,
|
||||
std::negation<is_std_span<Range>>,
|
||||
std::negation<is_std_array<Range>>,
|
||||
std::negation<std::is_array<q20::remove_cvref_t<Range>>>,
|
||||
is_compatible_range_helper<Range>
|
||||
>;
|
||||
|
||||
// constraints
|
||||
template <typename Iterator>
|
||||
using if_compatible_iterator = std::enable_if_t<
|
||||
is_compatible_iterator<Iterator>::value
|
||||
, bool>;
|
||||
template <typename Iterator, typename End>
|
||||
using if_compatible_iterator_and_sentinel = std::enable_if_t<
|
||||
is_compatible_iterator_and_sentinel<Iterator, End>::value
|
||||
, bool>;
|
||||
template <typename Range>
|
||||
using if_compatible_range = std::enable_if_t<is_compatible_range<Range>::value, bool>;
|
||||
}; // class QSpanCommon
|
||||
|
||||
template <typename T, std::size_t E>
|
||||
class QSpanBase : protected QSpanCommon<T>
|
||||
{
|
||||
static_assert(E < size_t{(std::numeric_limits<qsizetype>::max)()},
|
||||
"QSpan only supports extents that fit into the signed size type (qsizetype).");
|
||||
|
||||
struct Enabled_t { explicit Enabled_t() = default; };
|
||||
static inline constexpr Enabled_t Enable{};
|
||||
|
||||
template <typename S, std::size_t N>
|
||||
using if_compatible_array = std::enable_if_t<
|
||||
N == E && is_qualification_conversion_v<S, T>
|
||||
, bool>;
|
||||
|
||||
template <typename S>
|
||||
using if_qualification_conversion = std::enable_if_t<
|
||||
is_qualification_conversion_v<S, T>
|
||||
, bool>;
|
||||
protected:
|
||||
using Base = QSpanCommon<T>;
|
||||
|
||||
// data members:
|
||||
T *m_data;
|
||||
static constexpr qsizetype m_size = qsizetype(E);
|
||||
|
||||
// types and constants:
|
||||
// (in QSpan only)
|
||||
|
||||
// constructors (need to be public d/t the way ctor inheriting works):
|
||||
public:
|
||||
template <std::size_t E2 = E, std::enable_if_t<E2 == 0, bool> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr} {}
|
||||
|
||||
template <typename It, typename Base::template if_compatible_iterator<It> = true>
|
||||
explicit constexpr QSpanBase(It first, qsizetype count)
|
||||
: m_data{q20::to_address(first)}
|
||||
{
|
||||
Q_ASSERT(count == m_size);
|
||||
}
|
||||
|
||||
template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
|
||||
explicit constexpr QSpanBase(It first, End last)
|
||||
: QSpanBase(first, last - first) {}
|
||||
|
||||
template <size_t N, std::enable_if_t<N == E, bool> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
|
||||
: QSpanBase(arr, N) {}
|
||||
|
||||
template <typename S, size_t N, if_compatible_array<S, N> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
|
||||
: QSpanBase(arr.data(), N) {}
|
||||
|
||||
template <typename S, size_t N, if_compatible_array<S, N> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
|
||||
: QSpanBase(arr.data(), N) {}
|
||||
|
||||
template <typename Range, typename Base::template if_compatible_range<Range> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(Range &&r)
|
||||
: QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
|
||||
qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
|
||||
{}
|
||||
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(QSpan<S, E> other) noexcept
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(QSpan<S> other)
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
template <typename S, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::span<S> other)
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
#endif // __cpp_lib_span
|
||||
}; // class QSpanBase (fixed extent)
|
||||
|
||||
template <typename T>
|
||||
class QSpanBase<T, q20::dynamic_extent> : protected QSpanCommon<T>
|
||||
{
|
||||
template <typename S>
|
||||
using if_qualification_conversion = std::enable_if_t<
|
||||
is_qualification_conversion_v<S, T>
|
||||
, bool>;
|
||||
protected:
|
||||
using Base = QSpanCommon<T>;
|
||||
|
||||
// data members:
|
||||
T *m_data;
|
||||
qsizetype m_size;
|
||||
|
||||
// constructors (need to be public d/t the way ctor inheriting works):
|
||||
public:
|
||||
Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr}, m_size{0} {}
|
||||
|
||||
template <typename It, typename Base::template if_compatible_iterator<It> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(It first, qsizetype count)
|
||||
: m_data{q20::to_address(first)}, m_size{count} {}
|
||||
|
||||
template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(It first, End last)
|
||||
: QSpanBase(first, last - first) {}
|
||||
|
||||
template <size_t N>
|
||||
Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
|
||||
: QSpanBase(arr, N) {}
|
||||
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
|
||||
: QSpanBase(arr.data(), N) {}
|
||||
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
|
||||
: QSpanBase(arr.data(), N) {}
|
||||
|
||||
template <typename Range, typename Base::template if_compatible_range<Range> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(Range &&r)
|
||||
: QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
|
||||
qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
|
||||
{}
|
||||
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(QSpan<S, N> other) noexcept
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
|
||||
#if __cpp_lib_span
|
||||
template <typename S, size_t N, if_qualification_conversion<S> = true>
|
||||
Q_IMPLICIT constexpr QSpanBase(std::span<S, N> other) noexcept
|
||||
: QSpanBase(other.data(), other.size())
|
||||
{}
|
||||
#endif // __cpp_lib_span
|
||||
}; // class QSpanBase (dynamic extent)
|
||||
|
||||
} // namespace QSpanPrivate
|
||||
|
||||
template <typename T, std::size_t E>
|
||||
class QSpan
|
||||
#ifndef Q_QDOC
|
||||
: private QSpanPrivate::QSpanBase<T, E>
|
||||
#endif
|
||||
{
|
||||
using Base = QSpanPrivate::QSpanBase<T, E>;
|
||||
Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0,
|
||||
[[maybe_unused]] qsizetype n = 1) const
|
||||
{
|
||||
Q_ASSERT(pos >= 0);
|
||||
Q_ASSERT(pos <= size());
|
||||
Q_ASSERT(n >= 0);
|
||||
Q_ASSERT(n <= size() - pos);
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
static constexpr bool subspan_always_succeeds_v = N <= E && E != q20::dynamic_extent;
|
||||
public:
|
||||
// constants and types
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<element_type>;
|
||||
using size_type = qsizetype; // difference to std::span
|
||||
using difference_type = qptrdiff; // difference to std::span
|
||||
using pointer = element_type*;
|
||||
using const_pointer = const element_type*;
|
||||
using reference = element_type&;
|
||||
using const_reference = const element_type&;
|
||||
using iterator = pointer; // implementation-defined choice
|
||||
using const_iterator = const_pointer; // implementation-defined choice
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
static constexpr std::size_t extent = E;
|
||||
|
||||
// [span.cons], constructors, copy, and assignment
|
||||
using Base::Base;
|
||||
|
||||
// [span.obs]
|
||||
[[nodiscard]] constexpr size_type size() const noexcept { return this->m_size; }
|
||||
[[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(T); }
|
||||
[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
// [span.elem]
|
||||
[[nodiscard]] constexpr reference operator[](size_type idx) const
|
||||
{ verify(idx); return data()[idx]; }
|
||||
[[nodiscard]] constexpr reference front() const { verify(); return *data(); }
|
||||
[[nodiscard]] constexpr reference back() const { verify(); return data()[size() - 1]; }
|
||||
[[nodiscard]] constexpr pointer data() const noexcept { return this->m_data; }
|
||||
|
||||
// [span.iterators]
|
||||
[[nodiscard]] constexpr iterator begin() const noexcept { return data(); }
|
||||
[[nodiscard]] constexpr iterator end() const noexcept { return data() + size(); }
|
||||
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
|
||||
[[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
|
||||
[[nodiscard]] constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
|
||||
[[nodiscard]] constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
|
||||
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
|
||||
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
|
||||
|
||||
// [span.sub]
|
||||
template <std::size_t Count>
|
||||
[[nodiscard]] constexpr QSpan<T, Count> first() const
|
||||
noexcept(subspan_always_succeeds_v<Count>)
|
||||
{
|
||||
static_assert(Count <= E,
|
||||
"Count cannot be larger than the span's extent.");
|
||||
verify(0, Count);
|
||||
return QSpan<T, Count>{data(), Count};
|
||||
}
|
||||
|
||||
template <std::size_t Count>
|
||||
[[nodiscard]] constexpr QSpan<T, Count> last() const
|
||||
noexcept(subspan_always_succeeds_v<Count>)
|
||||
{
|
||||
static_assert(Count <= E,
|
||||
"Count cannot be larger than the span's extent.");
|
||||
verify(0, Count);
|
||||
return QSpan<T, Count>{data() + (size() - Count), Count};
|
||||
}
|
||||
|
||||
template <std::size_t Offset>
|
||||
[[nodiscard]] constexpr auto subspan() const
|
||||
noexcept(subspan_always_succeeds_v<Offset>)
|
||||
{
|
||||
static_assert(Offset <= E,
|
||||
"Offset cannot be larger than the span's extent.");
|
||||
verify(Offset, 0);
|
||||
if constexpr (E == q20::dynamic_extent)
|
||||
return QSpan<T>{data() + Offset, qsizetype(size() - Offset)};
|
||||
else
|
||||
return QSpan<T, E - Offset>{data() + Offset, qsizetype(E - Offset)};
|
||||
}
|
||||
|
||||
template <std::size_t Offset, std::size_t Count>
|
||||
[[nodiscard]] constexpr auto subspan() const
|
||||
noexcept(subspan_always_succeeds_v<Offset + Count>)
|
||||
{ return subspan<Offset>().template first<Count>(); }
|
||||
|
||||
[[nodiscard]] constexpr QSpan<T> first(size_type n) const { verify(0, n); return {data(), n}; }
|
||||
[[nodiscard]] constexpr QSpan<T> last(size_type n) const { verify(0, n); return {data() + (size() - n), n}; }
|
||||
[[nodiscard]] constexpr QSpan<T> subspan(size_type pos) const { verify(pos, 0); return {data() + pos, size() - pos}; }
|
||||
[[nodiscard]] constexpr QSpan<T> subspan(size_type pos, size_type n) const { return subspan(pos).first(n); }
|
||||
|
||||
// Qt-compatibility API:
|
||||
[[nodiscard]] bool isEmpty() const noexcept { return empty(); }
|
||||
// nullary first()/last() clash with first<>() and last<>(), so they're not provided for QSpan
|
||||
[[nodiscard]] constexpr QSpan<T> sliced(size_type pos) const { return subspan(pos); }
|
||||
[[nodiscard]] constexpr QSpan<T> sliced(size_type pos, size_type n) const { return subspan(pos, n); }
|
||||
|
||||
}; // class QSpan
|
||||
|
||||
// [span.deduct]
|
||||
template <class It, class EndOrSize>
|
||||
QSpan(It, EndOrSize) -> QSpan<std::remove_reference_t<q20::iter_reference_t<It>>>;
|
||||
template <class T, std::size_t N>
|
||||
QSpan(T (&)[N]) -> QSpan<T, N>;
|
||||
template <class T, std::size_t N>
|
||||
QSpan(std::array<T, N> &) -> QSpan<T, N>;
|
||||
template <class T, std::size_t N>
|
||||
QSpan(const std::array<T, N> &) -> QSpan<const T, N>;
|
||||
template <class R>
|
||||
QSpan(R&&) -> QSpan<std::remove_reference_t<QSpanPrivate::range_reference_t<R>>>;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSPAN_P_H
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "QtCore/qlist.h"
|
||||
#include "QtCore/qnamespace.h"
|
||||
#include "QtCore/qset.h"
|
||||
#include <QtCore/qspan.h>
|
||||
#include "QtCore/qstring.h"
|
||||
#include "QtCore/qvarlengtharray.h"
|
||||
|
||||
@ -33,7 +34,6 @@
|
||||
#include "private/qfont_p.h"
|
||||
#include "private/qtextformat_p.h"
|
||||
#include "private/qunicodetools_p.h"
|
||||
#include "private/qspan_p.h"
|
||||
#ifndef QT_BUILD_COMPAT_LIB
|
||||
#include "private/qtextdocument_p.h"
|
||||
#endif
|
||||
|
@ -16,11 +16,11 @@
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/qspan.h>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QTemporaryDir>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <private/qspan_p.h>
|
||||
#include <QtCore/private/qduplicatetracker_p.h>
|
||||
|
||||
#include <QTest>
|
||||
|
@ -4,6 +4,4 @@
|
||||
qt_internal_add_test(tst_qspan
|
||||
SOURCES
|
||||
tst_qspan.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <private/qspan_p.h>
|
||||
#include <QSpan>
|
||||
|
||||
#include <QList>
|
||||
#include <QTest>
|
||||
|
Loading…
x
Reference in New Issue
Block a user