Skip to content

Commit ce02416

Browse files
committed
QRM: specialize row_traits for std::array
std::tuple_element is implemented for std::array, so we end up using the tuple specialization of row_traits for a QRangeModel(std::array<T, N>). This is semantically wrong, but it also breaks the build when using a large array. The tuple specialization uses a helper to test if all elements in the tuple have the same type. With large arrays, this hits the compiler's limit for fold-expressions, which defaults with clang to 256. Specialize row_traits for std::array as well as C arrays; we know that all elements are of the same type. To avoid ambiguity, don't consider a std::array as a tuple. Introduce a array_like detector that is true for std::array and C arrays of any type and overload for_element_at using that detector (we have to, as we need the array as a universal reference). For the same reason, avoid the fold expression in meta_type_at if the row type is an array. All types are the same anyway, so we can just pick the first. Pick-to: 6.10 Change-Id: Ic49e222d7b3390f9e4cc15ec531f8a97981ab0d5 Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
1 parent c8a1e7b commit ce02416

File tree

3 files changed

+73
-10
lines changed

3 files changed

+73
-10
lines changed

src/corelib/itemmodels/qrangemodel_impl.h

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,22 @@ namespace QRangeModelDetails
302302

303303
template <typename T, typename = void>
304304
struct tuple_like : std::false_type {};
305+
template <typename T, std::size_t N>
306+
struct tuple_like<std::array<T, N>> : std::false_type {};
305307
template <typename T>
306308
struct tuple_like<T, std::void_t<std::tuple_element_t<0, wrapped_t<T>>>> : std::true_type {};
307309
template <typename T>
308310
[[maybe_unused]] static constexpr bool tuple_like_v = tuple_like<T>::value;
309311

312+
template <typename T, typename = void>
313+
struct array_like : std::false_type {};
314+
template <typename T, std::size_t N>
315+
struct array_like<std::array<T, N>> : std::true_type {};
316+
template <typename T, std::size_t N>
317+
struct array_like<T[N]> : std::true_type {};
318+
template <typename T>
319+
[[maybe_unused]] static constexpr bool array_like_v = array_like<T>::value;
320+
310321
template <typename T, typename = void>
311322
struct has_metaobject : std::false_type {};
312323
template <typename T>
@@ -556,9 +567,9 @@ namespace QRangeModelDetails
556567
struct row_traits<T, std::enable_if_t<tuple_like_v<T> && !has_metaobject_v<T>>>
557568
: tuple_row_traits<T> {};
558569

559-
// Specialization for C arrays
570+
// Specialization for C arrays and std::array
560571
template <typename T, std::size_t N>
561-
struct row_traits<T[N]>
572+
struct row_traits<std::array<T, N>>
562573
{
563574
static_assert(q20::in_range<int>(N));
564575
static constexpr int static_size = int(N);
@@ -567,6 +578,9 @@ namespace QRangeModelDetails
567578
static constexpr bool hasMetaObject = false;
568579
};
569580

581+
template <typename T, std::size_t N>
582+
struct row_traits<T[N]> : row_traits<std::array<T, N>> {};
583+
570584
template <typename T>
571585
struct metaobject_row_traits
572586
{
@@ -794,18 +808,21 @@ class QRangeModelImplBase : public QtPrivate::QQuasiVirtualInterface<QRangeModel
794808
using Self = QRangeModelImplBase;
795809
using QtPrivate::QQuasiVirtualInterface<Self>::Method;
796810
protected:
797-
// Helpers for calling a lambda with the tuple element at a runtime index.
798-
template <typename Tuple, typename F, size_t ...Is>
799-
static void call_at(Tuple &&tuple, size_t idx, std::index_sequence<Is...>, F &&function)
811+
// Helpers for calling a lambda with the element of a statically
812+
// sized range (tuple or array) with a runtime index.
813+
template <typename Tuple, typename F, std::size_t ...Is>
814+
static void call_at(Tuple &&tuple, std::size_t idx, std::index_sequence<Is...>, F &&function)
800815
{
816+
static_assert(QRangeModelDetails::tuple_like_v<q20::remove_cvref_t<Tuple>>);
801817
if (QRangeModelDetails::isValid(tuple))
802818
((Is == idx ? static_cast<void>(function(get<Is>(
803819
QRangeModelDetails::refTo(std::forward<Tuple>(tuple)))))
804820
: static_cast<void>(0)), ...);
805821
}
806822

807-
template <typename T, typename F>
808-
static auto for_element_at(T &&tuple, size_t idx, F &&function)
823+
template <typename T, typename F,
824+
std::enable_if_t<QRangeModelDetails::tuple_like_v<q20::remove_cvref_t<T>>, bool> = true>
825+
static auto for_element_at(T &&tuple, std::size_t idx, F &&function)
809826
{
810827
using type = QRangeModelDetails::wrapped_t<T>;
811828
constexpr size_t size = std::tuple_size_v<type>;
@@ -814,6 +831,14 @@ class QRangeModelImplBase : public QtPrivate::QQuasiVirtualInterface<QRangeModel
814831
std::forward<F>(function));
815832
}
816833

834+
template <typename Array, typename F,
835+
std::enable_if_t<QRangeModelDetails::array_like_v<q20::remove_cvref_t<Array>>, bool> = true>
836+
static auto for_element_at(Array &&array, std::size_t idx, F &&function)
837+
{
838+
Q_ASSERT(idx < array.size());
839+
function(q23::forward_like<Array>(array[idx]));
840+
}
841+
817842
// Get the QMetaType for a tuple-element at a runtime index.
818843
// Used in the headerData implementation.
819844
template <typename Tuple, std::size_t ...I>
@@ -825,9 +850,14 @@ class QRangeModelImplBase : public QtPrivate::QQuasiVirtualInterface<QRangeModel
825850
static constexpr QMetaType meta_type_at(size_t idx)
826851
{
827852
using type = QRangeModelDetails::wrapped_t<T>;
828-
constexpr auto size = std::tuple_size_v<type>;
829-
Q_ASSERT(idx < size);
830-
return makeMetaTypes<type>(std::make_index_sequence<size>{}).at(idx);
853+
if constexpr (QRangeModelDetails::array_like_v<type>) {
854+
Q_UNUSED(idx);
855+
return QMetaType::fromType<std::tuple_element_t<0, T>>();
856+
} else {
857+
constexpr auto size = std::tuple_size_v<type>;
858+
Q_ASSERT(idx < size);
859+
return makeMetaTypes<type>(std::make_index_sequence<size>{}).at(idx);
860+
}
831861
}
832862

833863
public:

tests/auto/corelib/itemmodels/qrangemodel/tst_qrangemodel.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ private slots:
6363
void moveColumns();
6464

6565
void inconsistentColumnCount();
66+
void largeArrays();
6667

6768
void tree_data();
6869
void tree();
@@ -1137,6 +1138,31 @@ void tst_QRangeModel::inconsistentColumnCount()
11371138
}
11381139
}
11391140

1141+
void tst_QRangeModel::largeArrays()
1142+
{
1143+
{
1144+
std::array<int, 10000> largeArray = {};
1145+
QRangeModel model(largeArray);
1146+
const QModelIndex index = model.index(int(largeArray.size() - 1), 0);
1147+
QCOMPARE(index.data(), 0);
1148+
}
1149+
1150+
{
1151+
int largeArray[10000] = {};
1152+
QRangeModel model(&largeArray);
1153+
const QModelIndex index = model.index(int(std::size(largeArray)) - 1, 0);
1154+
QCOMPARE(index.data(), 0);
1155+
}
1156+
1157+
{
1158+
std::array<std::array<int, 10000>, 1> largeColumn = {};
1159+
QRangeModel model(largeColumn);
1160+
const QModelIndex index = model.index(int(largeColumn.size() - 1),
1161+
int(largeColumn[0].size() - 1));
1162+
QCOMPARE(index.data(), 0);
1163+
}
1164+
}
1165+
11401166
enum class TreeProtocol {
11411167
ValueImplicit,
11421168
ValueReadOnly,

tests/manual/corelib/itemmodels/qrangemodel/main.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,20 @@ class ModelFactory : public QObject
192192

193193
std::vector<int> numbers = {1, 2, 3, 4, 5};
194194
QList<QString> strings = {u"one"_s, u"two"_s, u"three"_s};
195+
std::array<int, 1000000> largeArray = {};
195196

196197
public slots:
197198
QRangeModel *makeNumbers()
198199
{
199200
return new QRangeModel(&numbers);
200201
}
201202

203+
QRangeModel *makeLargeArray()
204+
{
205+
return new QRangeModel(&largeArray);
206+
}
207+
208+
202209
QRangeModel *makeStrings()
203210
{
204211
return new QRangeModel(std::ref(strings));

0 commit comments

Comments
 (0)