// 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 #include #include #include #include #include #include #include #include #ifdef __cpp_lib_span #include #endif #include 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 , 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; template struct is_qspan_helper : std::false_type {}; template struct is_qspan_helper> : std::true_type {}; template 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 struct is_std_array_helper> : std::true_type {}; template using is_std_array = is_std_array_helper>; template using is_qualification_conversion = std::is_convertible; // https://eel.is/c++draft/span.cons#note-1 template constexpr inline bool is_qualification_conversion_v = is_qualification_conversion::value; namespace AdlTester { #define MAKE_ADL_TEST(what) \ using std:: what; /* bring into scope */ \ template using what ## _result = decltype( what (std::declval())); \ /* 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 AdlTester::begin_result adl_begin(Range &&r) { using std::begin; return begin(r); } template AdlTester::data_result adl_data(Range &&r) { using std::data; return data(r); } template AdlTester::size_result 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 using iterator_t = decltype(QSpanPrivate::adl_begin(std::declval())); template using range_reference_t = q20::iter_reference_t>; template class QSpanCommon { protected: template 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_category >, is_qualification_conversion< std::remove_reference_t>, T > >; template using is_compatible_iterator_and_sentinel = std::conjunction< // ### C++20: extend to contiguous_iterators and real sentinels is_compatible_iterator, std::negation> >; template // wrap use of SFINAE-unfriendly iterator_t: struct is_compatible_range_helper : std::false_type {}; template struct is_compatible_range_helper>> : is_compatible_iterator> {}; template using is_compatible_range = std::conjunction< // ### C++20: extend to contiguous_iterators std::negation>, std::negation>, std::negation>, std::negation>>, is_compatible_range_helper >; // constraints template using if_compatible_iterator = std::enable_if_t< is_compatible_iterator::value , bool>; template using if_compatible_iterator_and_sentinel = std::enable_if_t< is_compatible_iterator_and_sentinel::value , bool>; template using if_compatible_range = std::enable_if_t::value, bool>; }; // class QSpanCommon template class QSpanBase : protected QSpanCommon { static_assert(E < size_t{(std::numeric_limits::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 using if_compatible_array = std::enable_if_t< N == E && is_qualification_conversion_v , bool>; template using if_qualification_conversion = std::enable_if_t< is_qualification_conversion_v , bool>; protected: using Base = QSpanCommon; // 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 = true> Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr} {} template = true> explicit constexpr QSpanBase(It first, qsizetype count) : m_data{q20::to_address(first)} { Q_ASSERT(count == m_size); } template = true> explicit constexpr QSpanBase(It first, End last) : QSpanBase(first, last - first) {} template = true> Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t (&arr)[N]) noexcept : QSpanBase(arr, N) {} template = true> Q_IMPLICIT constexpr QSpanBase(std::array &arr) noexcept : QSpanBase(arr.data(), N) {} template = true> Q_IMPLICIT constexpr QSpanBase(const std::array &arr) noexcept : QSpanBase(arr.data(), N) {} template = 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 = true> Q_IMPLICIT constexpr QSpanBase(QSpan other) noexcept : QSpanBase(other.data(), other.size()) {} template = true> Q_IMPLICIT constexpr QSpanBase(QSpan other) : QSpanBase(other.data(), other.size()) {} template , bool> = true> Q_IMPLICIT constexpr QSpanBase(std::initializer_list> il) : QSpanBase(il.begin(), il.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 class QSpanBase : protected QSpanCommon { template using if_qualification_conversion = std::enable_if_t< is_qualification_conversion_v , bool>; protected: using Base = QSpanCommon; // 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 = true> Q_IMPLICIT constexpr QSpanBase(It first, qsizetype count) : m_data{q20::to_address(first)}, m_size{count} {} template = true> Q_IMPLICIT constexpr QSpanBase(It first, End last) : QSpanBase(first, last - first) {} template Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t (&arr)[N]) noexcept : QSpanBase(arr, N) {} template = true> Q_IMPLICIT constexpr QSpanBase(std::array &arr) noexcept : QSpanBase(arr.data(), N) {} template = true> Q_IMPLICIT constexpr QSpanBase(const std::array &arr) noexcept : QSpanBase(arr.data(), N) {} template = 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 = true> Q_IMPLICIT constexpr QSpanBase(QSpan other) noexcept : QSpanBase(other.data(), other.size()) {} template , bool> = true> Q_IMPLICIT constexpr QSpanBase(std::initializer_list> il) noexcept : QSpanBase(il.begin(), il.size()) {} #ifdef __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 template class QSpan #ifndef Q_QDOC : private QSpanPrivate::QSpanBase #endif { using Base = QSpanPrivate::QSpanBase; 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 static constexpr bool subspan_always_succeeds_v = N <= E && E != q20::dynamic_extent; public: // constants and types using value_type = std::remove_cv_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; using const_reverse_iterator = std::reverse_iterator; static constexpr std::size_t extent = E; // [span.cons], constructors, copy, and assignment using Base::Base; #ifdef Q_QDOC template using if_compatible_iterator = bool; template using if_qualification_conversion = bool; template using if_compatible_range = bool; template = true> constexpr QSpan(It first, qsizetype count); template = true> constexpr QSpan(It first, It last); template constexpr QSpan(q20::type_identity_t (&arr)[N]) noexcept; template = true> constexpr QSpan(std::array &arr) noexcept; template = true> constexpr QSpan(const std::array &arr) noexcept; template = true> constexpr QSpan(Range &&r); template = true> constexpr QSpan(QSpan other) noexcept; template = true> constexpr QSpan(std::span other) noexcept; constexpr QSpan(std::initializer_list 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 [[nodiscard]] constexpr QSpan first() const noexcept(subspan_always_succeeds_v) { static_assert(Count <= E, "Count cannot be larger than the span's extent."); verify(0, Count); return QSpan{data(), Count}; } template [[nodiscard]] constexpr QSpan last() const noexcept(subspan_always_succeeds_v) { static_assert(Count <= E, "Count cannot be larger than the span's extent."); verify(0, Count); return QSpan{data() + (size() - Count), Count}; } template [[nodiscard]] constexpr auto subspan() const noexcept(subspan_always_succeeds_v) { static_assert(Offset <= E, "Offset cannot be larger than the span's extent."); verify(Offset, 0); if constexpr (E == q20::dynamic_extent) return QSpan{data() + Offset, qsizetype(size() - Offset)}; else return QSpan{data() + Offset, qsizetype(E - Offset)}; } template [[nodiscard]] constexpr auto subspan() const noexcept(subspan_always_succeeds_v) { return subspan().template first(); } [[nodiscard]] constexpr QSpan first(size_type n) const { verify(0, n); return {data(), n}; } [[nodiscard]] constexpr QSpan last(size_type n) const { verify(0, n); return {data() + (size() - n), n}; } [[nodiscard]] constexpr QSpan subspan(size_type pos) const { verify(pos, 0); return {data() + pos, size() - pos}; } [[nodiscard]] constexpr QSpan 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 sliced(size_type pos) const { return subspan(pos); } [[nodiscard]] constexpr QSpan sliced(size_type pos, size_type n) const { return subspan(pos, n); } }; // class QSpan // [span.deduct] template QSpan(It, EndOrSize) -> QSpan>>; template QSpan(T (&)[N]) -> QSpan; template QSpan(std::array &) -> QSpan; template QSpan(const std::array &) -> QSpan; template QSpan(R&&) -> QSpan>>; QT_END_NAMESPACE #endif // QSPAN_H