qt6-bb10/src/corelib/tools/qspan.h

473 lines
19 KiB
C++

// 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 <initializer_list>
#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;
namespace AdlTester {
#define MAKE_ADL_TEST(what) \
using std:: what; /* bring into scope */ \
template <typename T> using what ## _result = decltype( what (std::declval<T&&>())); \
/* end */
MAKE_ADL_TEST(begin)
MAKE_ADL_TEST(data)
MAKE_ADL_TEST(size)
#undef MAKE_ADL_TEST
}
// Replacements for std::ranges::XXX(), but only bringing in ADL XXX()s,
// not doing the extra work C++20 requires
template <typename Range>
AdlTester::begin_result<Range> adl_begin(Range &&r) { using std::begin; return begin(r); }
template <typename Range>
AdlTester::data_result<Range> adl_data(Range &&r) { using std::data; return data(r); }
template <typename Range>
AdlTester::size_result<Range> 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<
// ### C++20: extend to contiguous_iteratorss
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<
// ### C++20: extend to contiguous_iterators and real sentinels
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<
// ### C++20: extend to 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).");
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())
{}
template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true>
Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il)
: QSpanBase(il.begin(), il.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())
{}
template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true>
Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il) noexcept
: QSpanBase(il.begin(), il.size())
{}
#ifdef __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 value_type = std::remove_cv_t<T>;
#ifdef QT_COMPILER_HAS_LWG3346
using iterator_concept = std::contiguous_iterator_tag;
using element_type = T;
#endif
using size_type = qsizetype; // difference to std::span
using difference_type = qptrdiff; // difference to std::span
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
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;
constexpr QSpan(std::initializer_list<value_type> il);
#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); }
private:
// [span.objectrep]
[[nodiscard]] friend
QSpan<const std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>
as_bytes(QSpan s) noexcept
{
using R = QSpan<const std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>;
return R{reinterpret_cast<const std::byte *>(s.data()), s.size_bytes()};
}
template <typename U>
using if_mutable = std::enable_if_t<!std::is_const_v<U>, bool>;
#ifndef Q_QDOC
template <typename T2 = T, if_mutable<T2> = true>
#endif
[[nodiscard]] friend
QSpan<std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>
as_writable_bytes(QSpan s) noexcept
{
using R = QSpan<std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>;
return R{reinterpret_cast<std::byte *>(s.data()), s.size_bytes()};
}
}; // 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