QGIM: add reusable concepts for unifying pointer and reference types
Standardize the way to get a reference or pointer to an element, and use that to implement begin/end helpers that work with values of and pointers/references to ranges. This allows us to support copies, references, raw pointers, and smart pointers holding a range, both as the data itself and as rows of data. Change-Id: Ief680d87fcfd6fcabcfe2fa4841d3641b49f0c43 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
ec200659da
commit
424d6ebf13
@ -28,7 +28,7 @@ public:
|
||||
MultiColumn<T>>, bool>;
|
||||
|
||||
template <typename V = T,
|
||||
std::enable_if_t<std::is_constructible_v<bool, V>, bool> = true>
|
||||
std::enable_if_t<QGenericItemModelDetails::is_validatable<V>::value, bool> = true>
|
||||
constexpr explicit operator bool() const noexcept { return bool(data); }
|
||||
|
||||
// unconstrained on size_t I, gcc internal error #3280
|
||||
@ -166,9 +166,9 @@ class QGenericItemModelImpl : public QGenericItemModelImplBase
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QGenericItemModelImpl)
|
||||
public:
|
||||
using range_type = QGenericItemModelDetails::remove_ptr_and_ref_t<Range>;
|
||||
using row_reference = decltype(*std::begin(std::declval<range_type&>()));
|
||||
using const_row_reference = decltype(*std::cbegin(std::declval<range_type&>()));
|
||||
using range_type = QGenericItemModelDetails::wrapped_t<Range>;
|
||||
using row_reference = decltype(*QGenericItemModelDetails::begin(std::declval<range_type&>()));
|
||||
using const_row_reference = decltype(*QGenericItemModelDetails::cbegin(std::declval<range_type&>()));
|
||||
using row_type = std::remove_reference_t<row_reference>;
|
||||
|
||||
protected:
|
||||
@ -177,24 +177,29 @@ protected:
|
||||
const Structure& that() const { return static_cast<const Structure &>(*this); }
|
||||
|
||||
template <typename C>
|
||||
static constexpr auto size(const C &c)
|
||||
static constexpr int size(const C &c)
|
||||
{
|
||||
if constexpr (QGenericItemModelDetails::test_size<C>())
|
||||
if (!QGenericItemModelDetails::isValid(c))
|
||||
return 0;
|
||||
|
||||
if constexpr (QGenericItemModelDetails::test_size<C>()) {
|
||||
return std::size(c);
|
||||
else
|
||||
} else {
|
||||
#if defined(__cpp_lib_ranges)
|
||||
return std::ranges::distance(std::begin(c), std::end(c));
|
||||
return int(std::ranges::distance(QGenericItemModelDetails::begin(c),
|
||||
QGenericItemModelDetails::end(c)));
|
||||
#else
|
||||
return std::distance(std::begin(c), std::end(c));
|
||||
return int(std::distance(QGenericItemModelDetails::begin(c),
|
||||
QGenericItemModelDetails::end(c)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
friend class tst_QGenericItemModel;
|
||||
using range_features = QGenericItemModelDetails::range_traits<range_type>;
|
||||
using row_features = QGenericItemModelDetails::range_traits<row_type>;
|
||||
|
||||
using row_traits = QGenericItemModelDetails::row_traits<q20::remove_cvref_t<
|
||||
std::remove_pointer_t<row_type>>>;
|
||||
using wrapped_row_type = QGenericItemModelDetails::wrapped_t<row_type>;
|
||||
using row_features = QGenericItemModelDetails::range_traits<wrapped_row_type>;
|
||||
using row_traits = QGenericItemModelDetails::row_traits<std::remove_cv_t<wrapped_row_type>>;
|
||||
|
||||
static constexpr bool isMutable()
|
||||
{
|
||||
@ -204,7 +209,9 @@ protected:
|
||||
}
|
||||
|
||||
static constexpr int static_row_count = QGenericItemModelDetails::static_size_v<range_type>;
|
||||
static constexpr bool rows_are_pointers = std::is_pointer_v<row_type>;
|
||||
static constexpr bool rows_are_raw_pointers = std::is_pointer_v<row_type>;
|
||||
static constexpr bool rows_are_owning_or_raw_pointers =
|
||||
QGenericItemModelDetails::is_owning_or_raw_pointer<row_type>();
|
||||
static constexpr int static_column_count = QGenericItemModelDetails::static_size_v<row_type>;
|
||||
static constexpr bool one_dimensional_range = static_column_count == 0;
|
||||
|
||||
@ -213,8 +220,8 @@ protected:
|
||||
|
||||
// A row might be a value (or range of values), or a pointer.
|
||||
// row_ptr is always a pointer, and const_row_ptr is a pointer to const.
|
||||
using row_ptr = std::conditional_t<rows_are_pointers, row_type, row_type *>;
|
||||
using const_row_ptr = const std::remove_pointer_t<row_type> *;
|
||||
using row_ptr = wrapped_row_type *;
|
||||
using const_row_ptr = const wrapped_row_type *;
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool has_metaobject =
|
||||
@ -444,7 +451,7 @@ public:
|
||||
if (index.isValid()) {
|
||||
const_row_reference row = rowData(index);
|
||||
if constexpr (dynamicColumns())
|
||||
readData(*std::next(std::cbegin(row), index.column()));
|
||||
readData(*QGenericItemModelDetails::cpos(row, index.column()));
|
||||
else if constexpr (one_dimensional_range)
|
||||
readData(row);
|
||||
else if (QGenericItemModelDetails::isValid(row))
|
||||
@ -507,7 +514,7 @@ public:
|
||||
if (index.isValid()) {
|
||||
const_row_reference row = rowData(index);
|
||||
if constexpr (dynamicColumns())
|
||||
readItemData(*std::next(std::cbegin(row), index.column()));
|
||||
readItemData(*QGenericItemModelDetails::cpos(row, index.column()));
|
||||
else if constexpr (one_dimensional_range)
|
||||
readItemData(row);
|
||||
else if (QGenericItemModelDetails::isValid(row))
|
||||
@ -576,7 +583,7 @@ public:
|
||||
|
||||
row_reference row = rowData(index);
|
||||
if constexpr (dynamicColumns()) {
|
||||
success = writeData(*std::next(std::begin(row), index.column()));
|
||||
success = writeData(*QGenericItemModelDetails::pos(row, index.column()));
|
||||
} else if constexpr (one_dimensional_range) {
|
||||
success = writeData(row);
|
||||
} else if (QGenericItemModelDetails::isValid(row)) {
|
||||
@ -688,7 +695,7 @@ public:
|
||||
|
||||
row_reference row = rowData(index);
|
||||
if constexpr (dynamicColumns()) {
|
||||
success = writeItemData(*std::next(std::begin(row), index.column()));
|
||||
success = writeItemData(*QGenericItemModelDetails::pos(row, index.column()));
|
||||
} else if constexpr (one_dimensional_range) {
|
||||
success = writeItemData(row);
|
||||
} else if (QGenericItemModelDetails::isValid(row)) {
|
||||
@ -740,7 +747,7 @@ public:
|
||||
|
||||
row_reference row = rowData(index);
|
||||
if constexpr (dynamicColumns()) {
|
||||
success = clearData(*std::next(std::begin(row), index.column()));
|
||||
success = clearData(*QGenericItemModelDetails::pos(row, index.column()));
|
||||
} else if constexpr (one_dimensional_range) {
|
||||
success = clearData(row);
|
||||
} else if (QGenericItemModelDetails::isValid(row)) {
|
||||
@ -766,8 +773,10 @@ public:
|
||||
return false;
|
||||
|
||||
beginInsertColumns(parent, column, column + count - 1);
|
||||
for (auto &child : *children)
|
||||
child.insert(std::next(std::begin(child), column), count, {});
|
||||
for (auto &child : *children) {
|
||||
auto it = QGenericItemModelDetails::pos(child, column);
|
||||
QGenericItemModelDetails::refTo(child).insert(it, count, {});
|
||||
}
|
||||
endInsertColumns();
|
||||
return true;
|
||||
}
|
||||
@ -786,8 +795,8 @@ public:
|
||||
|
||||
beginRemoveColumns(parent, column, column + count - 1);
|
||||
for (auto &child : *children) {
|
||||
const auto start = std::next(std::begin(child), column);
|
||||
child.erase(start, std::next(start, count));
|
||||
const auto start = QGenericItemModelDetails::pos(child, column);
|
||||
QGenericItemModelDetails::refTo(child).erase(start, std::next(start, count));
|
||||
}
|
||||
endRemoveColumns();
|
||||
return true;
|
||||
@ -818,10 +827,9 @@ public:
|
||||
}
|
||||
|
||||
for (auto &child : *children) {
|
||||
const auto begin = std::begin(child);
|
||||
const auto first = std::next(begin, sourceColumn);
|
||||
const auto middle = std::next(begin, sourceColumn + count);
|
||||
const auto last = std::next(begin, destColumn);
|
||||
const auto first = QGenericItemModelDetails::pos(child, sourceColumn);
|
||||
const auto middle = std::next(first, count);
|
||||
const auto last = QGenericItemModelDetails::pos(child, destColumn);
|
||||
|
||||
if (sourceColumn < destColumn) // moving right
|
||||
std::rotate(first, middle, last);
|
||||
@ -849,12 +857,12 @@ public:
|
||||
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
|
||||
const auto pos = std::next(std::begin(*children), row);
|
||||
const auto pos = QGenericItemModelDetails::pos(children, row);
|
||||
if constexpr (range_features::has_insert_range) {
|
||||
EmptyRowGenerator first{0, that(), parent};
|
||||
EmptyRowGenerator last{count, that(), parent};
|
||||
children->insert(pos, first, last);
|
||||
} else if constexpr (rows_are_pointers) {
|
||||
} else if constexpr (rows_are_raw_pointers) {
|
||||
auto start = children->insert(pos, count, nullptr);
|
||||
auto end = std::next(start, count);
|
||||
for (auto it = start; it != end; ++it)
|
||||
@ -862,7 +870,7 @@ public:
|
||||
} else {
|
||||
children->insert(pos, count, that().makeEmptyRow(parent));
|
||||
}
|
||||
if constexpr (!rows_are_pointers) {
|
||||
if constexpr (!rows_are_raw_pointers) {
|
||||
// fix the parent in all children of the modified row, as the
|
||||
// references back to the parent might have become invalid.
|
||||
that().resetParentInChildren(children);
|
||||
@ -899,15 +907,15 @@ public:
|
||||
}
|
||||
}
|
||||
{ // erase invalidates iterators
|
||||
const auto begin = std::next(std::begin(*children), row);
|
||||
const auto begin = QGenericItemModelDetails::pos(children, row);
|
||||
const auto end = std::next(begin, count);
|
||||
if constexpr (rows_are_pointers)
|
||||
if constexpr (rows_are_raw_pointers)
|
||||
that().deleteRemovedRows(begin, end);
|
||||
children->erase(begin, end);
|
||||
}
|
||||
// fix the parent in all children of the modified row, as the
|
||||
// references back to the parent might have become invalid.
|
||||
if constexpr (!rows_are_pointers)
|
||||
if constexpr (!rows_are_raw_pointers)
|
||||
that().resetParentInChildren(children);
|
||||
if constexpr (dynamicColumns()) {
|
||||
if (callEndRemoveColumns) {
|
||||
@ -945,16 +953,16 @@ public:
|
||||
if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
|
||||
return false;
|
||||
|
||||
const auto first = std::next(std::begin(*source), sourceRow);
|
||||
const auto first = QGenericItemModelDetails::pos(source, sourceRow);
|
||||
const auto middle = std::next(first, count);
|
||||
const auto last = std::next(std::begin(*source), destRow);
|
||||
const auto last = QGenericItemModelDetails::pos(source, destRow);
|
||||
|
||||
if (sourceRow < destRow) // moving down
|
||||
std::rotate(first, middle, last);
|
||||
else // moving up
|
||||
std::rotate(last, first, middle);
|
||||
|
||||
if constexpr (!rows_are_pointers)
|
||||
if constexpr (!rows_are_raw_pointers)
|
||||
that().resetParentInChildren(source);
|
||||
|
||||
endMoveRows();
|
||||
@ -967,7 +975,7 @@ public:
|
||||
protected:
|
||||
~QGenericItemModelImpl()
|
||||
{
|
||||
if constexpr (rows_are_pointers && !std::is_pointer_v<Range>) {
|
||||
if constexpr (rows_are_raw_pointers && !std::is_pointer_v<Range>) {
|
||||
// If data with rows as pointers was moved in, then we own it and
|
||||
// have to delete those rows.
|
||||
using ref = decltype(std::forward<Range>(std::declval<range_type>()));
|
||||
@ -1212,7 +1220,7 @@ protected:
|
||||
|
||||
const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
|
||||
const auto &parentSiblings = childrenOf(grandParent);
|
||||
const auto it = std::next(std::cbegin(parentSiblings), parent.row());
|
||||
const auto it = QGenericItemModelDetails::cpos(parentSiblings, parent.row());
|
||||
return this->createIndex(row, column, QGenericItemModelDetails::pointerTo(*it));
|
||||
}
|
||||
|
||||
@ -1228,19 +1236,20 @@ protected:
|
||||
|
||||
// get the siblings of the parent via the grand parent
|
||||
const_row_ptr grandParent;
|
||||
if constexpr (Base::rows_are_pointers)
|
||||
if constexpr (Base::rows_are_raw_pointers)
|
||||
grandParent = m_protocol.parentRow(parentRow);
|
||||
else
|
||||
grandParent = m_protocol.parentRow(*parentRow);
|
||||
const range_type &parentSiblings = childrenOf(grandParent);
|
||||
// find the index of parentRow
|
||||
const auto begin = std::cbegin(parentSiblings);
|
||||
const auto end = std::cend(parentSiblings);
|
||||
const auto begin = QGenericItemModelDetails::cbegin(parentSiblings);
|
||||
const auto end = QGenericItemModelDetails::cend(parentSiblings);
|
||||
const auto it = std::find_if(begin, end, [parentRow](auto &&s){
|
||||
return QGenericItemModelDetails::pointerTo(s) == parentRow;
|
||||
});
|
||||
if (it != end)
|
||||
return this->createIndex(std::distance(begin, it), 0, grandParent);
|
||||
return this->createIndex(std::distance(begin, it), 0,
|
||||
QGenericItemModelDetails::pointerTo(grandParent));
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -1266,7 +1275,7 @@ protected:
|
||||
// We must not insert rows if we cannot adjust the parents of the
|
||||
// children of the following rows. We don't have to do that if the
|
||||
// range operates on pointers.
|
||||
return (Base::rows_are_pointers || tree_traits::has_setParentRow)
|
||||
return (Base::rows_are_raw_pointers || tree_traits::has_setParentRow)
|
||||
&& Base::dynamicRows() && range_features::has_insert;
|
||||
}
|
||||
|
||||
@ -1275,7 +1284,7 @@ protected:
|
||||
// We must not remove rows if we cannot adjust the parents of the
|
||||
// children of the following rows. We don't have to do that if the
|
||||
// range operates on pointers.
|
||||
return (Base::rows_are_pointers || tree_traits::has_setParentRow)
|
||||
return (Base::rows_are_raw_pointers || tree_traits::has_setParentRow)
|
||||
&& Base::dynamicRows() && range_features::has_erase;
|
||||
}
|
||||
|
||||
@ -1295,7 +1304,7 @@ protected:
|
||||
// If rows are pointers, then reference to the parent row don't
|
||||
// change, so we can move them around freely. Otherwise we need to
|
||||
// be able to explicitly update the parent pointer.
|
||||
if constexpr (!Base::rows_are_pointers && !tree_traits::has_setParentRow) {
|
||||
if constexpr (!Base::rows_are_raw_pointers && !tree_traits::has_setParentRow) {
|
||||
return false;
|
||||
} else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
|
||||
return false;
|
||||
@ -1309,9 +1318,9 @@ protected:
|
||||
|
||||
// If we can insert data from another range into, then
|
||||
// use that to move the old data over.
|
||||
const auto destStart = std::next(std::begin(*destination), destRow);
|
||||
const auto destStart = QGenericItemModelDetails::pos(destination, destRow);
|
||||
if constexpr (range_features::has_insert_range) {
|
||||
const auto sourceStart = std::next(std::begin(*source), sourceRow);
|
||||
const auto sourceStart = QGenericItemModelDetails::pos(*source, sourceRow);
|
||||
const auto sourceEnd = std::next(sourceStart, count);
|
||||
|
||||
destination->insert(destStart, std::move_iterator(sourceStart),
|
||||
@ -1339,9 +1348,9 @@ protected:
|
||||
|
||||
// move the data over and update the parent pointer
|
||||
{
|
||||
const auto writeStart = std::next(std::begin(*destination), destRow);
|
||||
const auto writeStart = QGenericItemModelDetails::pos(destination, destRow);
|
||||
const auto writeEnd = std::next(writeStart, count);
|
||||
const auto sourceStart = std::next(std::begin(*source), sourceRow);
|
||||
const auto sourceStart = QGenericItemModelDetails::pos(source, sourceRow);
|
||||
const auto sourceEnd = std::next(sourceStart, count);
|
||||
|
||||
for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
|
||||
@ -1359,7 +1368,7 @@ protected:
|
||||
// ranges, as the references to the entries might have become invalid.
|
||||
// We don't have to do that if the rows are pointers, as in that case
|
||||
// the references to the entries are stable.
|
||||
if constexpr (!Base::rows_are_pointers) {
|
||||
if constexpr (!Base::rows_are_raw_pointers) {
|
||||
resetParentInChildren(destination);
|
||||
resetParentInChildren(source);
|
||||
}
|
||||
@ -1384,8 +1393,8 @@ protected:
|
||||
template <typename R>
|
||||
void destroyOwnedModel(R &&range)
|
||||
{
|
||||
const auto begin = std::begin(range);
|
||||
const auto end = std::end(range);
|
||||
const auto begin = QGenericItemModelDetails::begin(range);
|
||||
const auto end = QGenericItemModelDetails::end(range);
|
||||
deleteRemovedRows(begin, end);
|
||||
}
|
||||
|
||||
@ -1399,8 +1408,8 @@ protected:
|
||||
void resetParentInChildren(range_type *children)
|
||||
{
|
||||
if constexpr (tree_traits::has_setParentRow) {
|
||||
const auto begin = std::begin(*children);
|
||||
const auto end = std::end(*children);
|
||||
const auto begin = QGenericItemModelDetails::begin(*children);
|
||||
const auto end = QGenericItemModelDetails::end(*children);
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
if (auto &maybeChildren = m_protocol.childRows(*it)) {
|
||||
QModelIndexList fromIndexes;
|
||||
@ -1431,7 +1440,7 @@ protected:
|
||||
const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
|
||||
const range_type &siblings = childrenOf(parentRow);
|
||||
Q_ASSERT(index.row() < int(Base::size(siblings)));
|
||||
return *(std::next(std::cbegin(siblings), index.row()));
|
||||
return *QGenericItemModelDetails::cpos(siblings, index.row());
|
||||
}
|
||||
|
||||
decltype(auto) rowDataImpl(const QModelIndex &index)
|
||||
@ -1439,13 +1448,13 @@ protected:
|
||||
row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
|
||||
range_type &siblings = childrenOf(parentRow);
|
||||
Q_ASSERT(index.row() < int(Base::size(siblings)));
|
||||
return *(std::next(std::begin(siblings), index.row()));
|
||||
return *QGenericItemModelDetails::pos(siblings, index.row());
|
||||
}
|
||||
|
||||
auto childRangeImpl(const QModelIndex &index) const
|
||||
{
|
||||
const auto &row = this->rowData(index);
|
||||
if constexpr (Base::rows_are_pointers) {
|
||||
if constexpr (Base::rows_are_raw_pointers) {
|
||||
if (!row)
|
||||
return static_cast<const range_type *>(nullptr);
|
||||
}
|
||||
@ -1456,7 +1465,7 @@ protected:
|
||||
auto childRangeImpl(const QModelIndex &index)
|
||||
{
|
||||
auto &row = this->rowData(index);
|
||||
if constexpr (Base::rows_are_pointers) {
|
||||
if constexpr (Base::rows_are_raw_pointers) {
|
||||
if (!row)
|
||||
return static_cast<range_type *>(nullptr);
|
||||
}
|
||||
@ -1471,7 +1480,7 @@ protected:
|
||||
{
|
||||
if (!row)
|
||||
return *this->m_data.model();
|
||||
if constexpr (Base::rows_are_pointers)
|
||||
if constexpr (Base::rows_are_raw_pointers)
|
||||
return *m_protocol.childRows(row);
|
||||
else
|
||||
return *m_protocol.childRows(*row);
|
||||
@ -1482,7 +1491,7 @@ private:
|
||||
{
|
||||
if (!row)
|
||||
return *this->m_data.model();
|
||||
if constexpr (Base::rows_are_pointers)
|
||||
if constexpr (Base::rows_are_raw_pointers)
|
||||
return *m_protocol.childRows(row);
|
||||
else
|
||||
return *m_protocol.childRows(*row);
|
||||
@ -1515,7 +1524,7 @@ protected:
|
||||
QModelIndex indexImpl(int row, int column, const QModelIndex &) const
|
||||
{
|
||||
if constexpr (Base::dynamicColumns()) {
|
||||
if (column < int(Base::size(*std::next(std::cbegin(*this->m_data.model()), row))))
|
||||
if (column < int(Base::size(*QGenericItemModelDetails::cpos(*this->m_data.model(), row))))
|
||||
return this->createIndex(row, column);
|
||||
// if we got here, then column < columnCount(), but this row is to short
|
||||
qCritical("QGenericItemModel: Column-range at row %d is not large enough!", row);
|
||||
@ -1546,7 +1555,7 @@ protected:
|
||||
if constexpr (Base::dynamicColumns()) {
|
||||
return int(Base::size(*this->m_data.model()) == 0
|
||||
? 0
|
||||
: Base::size(*std::cbegin(*this->m_data.model())));
|
||||
: Base::size(*QGenericItemModelDetails::cbegin(*this->m_data.model())));
|
||||
} else if constexpr (Base::one_dimensional_range) {
|
||||
return row_traits::fixed_size();
|
||||
} else {
|
||||
@ -1602,8 +1611,8 @@ protected:
|
||||
template <typename R>
|
||||
void destroyOwnedModel(R &&range)
|
||||
{
|
||||
const auto begin = std::begin(range);
|
||||
const auto end = std::end(range);
|
||||
const auto begin = QGenericItemModelDetails::begin(range);
|
||||
const auto end = QGenericItemModelDetails::end(range);
|
||||
for (auto it = begin; it != end; ++it)
|
||||
delete *it;
|
||||
}
|
||||
@ -1617,13 +1626,13 @@ protected:
|
||||
decltype(auto) rowDataImpl(const QModelIndex &index) const
|
||||
{
|
||||
Q_ASSERT(index.row() < int(Base::size(*this->m_data.model())));
|
||||
return *(std::next(std::cbegin(*this->m_data.model()), index.row()));
|
||||
return *QGenericItemModelDetails::cpos(*this->m_data.model(), index.row());
|
||||
}
|
||||
|
||||
decltype(auto) rowDataImpl(const QModelIndex &index)
|
||||
{
|
||||
Q_ASSERT(index.row() < int(Base::size(*this->m_data.model())));
|
||||
return *(std::next(std::begin(*this->m_data.model()), index.row()));
|
||||
return *QGenericItemModelDetails::pos(*this->m_data.model(), index.row());
|
||||
}
|
||||
|
||||
auto childRangeImpl(const QModelIndex &) const
|
||||
|
@ -30,14 +30,93 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QGenericItemModelDetails
|
||||
{
|
||||
template <typename T, size_t N> static decltype(auto) refTo(T (&t)[N]) { return t; }
|
||||
template <typename T> static T&& refTo(T&& t) { return std::forward<T>(t); }
|
||||
template <typename T> static T& refTo(std::reference_wrapper<T> t) { return t.get(); }
|
||||
// template <typename T> static auto refTo(T& t) -> decltype(*t) { return *t; }
|
||||
template <typename T, template <typename...> typename... Templates>
|
||||
struct is_any_of_impl : std::false_type {};
|
||||
|
||||
template <typename T> static auto pointerTo(T *t) { return t; }
|
||||
template <typename T> static auto pointerTo(T &t) { return std::addressof(refTo(t)); }
|
||||
template <typename T> static auto pointerTo(const T &&t) = delete;
|
||||
template <template <typename...> typename Template,
|
||||
typename... Params,
|
||||
template <typename...> typename... Templates>
|
||||
struct is_any_of_impl<Template<Params...>, Template, Templates...> : std::true_type {};
|
||||
|
||||
template <typename T,
|
||||
template <typename...> typename Template,
|
||||
template <typename...> typename... Templates>
|
||||
struct is_any_of_impl<T, Template, Templates...> : is_any_of_impl<T, Templates...> {};
|
||||
|
||||
template <typename T, template <typename...> typename... Templates>
|
||||
using is_any_of = is_any_of_impl<std::remove_cv_t<T>, Templates...>;
|
||||
|
||||
template <typename T>
|
||||
using is_validatable = std::is_constructible<bool, T>;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_smart_ptr : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_smart_ptr<T,
|
||||
std::enable_if_t<std::conjunction_v<
|
||||
std::is_pointer<decltype(std::declval<T&>().get())>,
|
||||
std::is_same<decltype(*std::declval<T&>().get()), decltype(*std::declval<T&>())>,
|
||||
is_validatable<T>
|
||||
>>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
// TODO: shouldn't we check is_smart_ptr && !is_copy_constructible && !is_copy_assignable
|
||||
// to support users-specific ptrs?
|
||||
template <typename T>
|
||||
using is_any_unique_ptr = is_any_of<T, std::unique_ptr, QScopedPointer>;
|
||||
|
||||
template <typename T>
|
||||
using is_any_shared_ptr = is_any_of<T, std::shared_ptr, QSharedPointer,
|
||||
QExplicitlySharedDataPointer, QSharedDataPointer>;
|
||||
|
||||
template <typename T>
|
||||
using is_owning_or_raw_pointer = std::disjunction<is_any_shared_ptr<T>, is_any_unique_ptr<T>, std::is_pointer<T>>;
|
||||
|
||||
|
||||
template <typename T>
|
||||
static auto pointerTo(T&& t) {
|
||||
using Type = q20::remove_cvref_t<T>;
|
||||
if constexpr (is_any_of<Type, std::optional>())
|
||||
return t ? std::addressof(*std::forward<T>(t)) : nullptr;
|
||||
else if constexpr (std::is_pointer<Type>())
|
||||
return t;
|
||||
else if constexpr (is_smart_ptr<Type>())
|
||||
return t.get();
|
||||
else if constexpr (is_any_of<Type, std::reference_wrapper>())
|
||||
return std::addressof(t.get());
|
||||
else
|
||||
return std::addressof(std::forward<T>(t));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using wrapped_t = std::remove_pointer_t<decltype(pointerTo(std::declval<T&>()))>;
|
||||
|
||||
template <typename T>
|
||||
using is_wrapped = std::negation<std::is_same<wrapped_t<T>, std::remove_reference_t<T>>>;
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool isValid(const T &t) noexcept
|
||||
{
|
||||
if constexpr (is_validatable<T>())
|
||||
return bool(t);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static decltype(auto) refTo(T&& t) {
|
||||
Q_ASSERT(isValid(t));
|
||||
// it's allowed to move only if the object holds unique ownership of the wrapped data
|
||||
using Type = q20::remove_cvref_t<T>;
|
||||
if constexpr (is_any_of<T, std::optional>())
|
||||
return *std::forward<T>(t); // let std::optional resolve dereferencing
|
||||
if constexpr (!is_wrapped<Type>() || is_any_unique_ptr<Type>())
|
||||
return q23::forward_like<T>(*pointerTo(t));
|
||||
else
|
||||
return *pointerTo(t);
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
auto key(It&& it) -> decltype(it.key()) { return it.key(); }
|
||||
@ -49,14 +128,25 @@ namespace QGenericItemModelDetails
|
||||
template <typename It>
|
||||
auto value(It&& it) -> decltype((it->second)) { return it->second; }
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool isValid(const T &t) noexcept
|
||||
{
|
||||
if constexpr (std::is_constructible_v<bool, T>)
|
||||
return bool(t);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
// use our own version of begin/end so that we can overload for pointers
|
||||
template <typename C>
|
||||
static auto begin(C &&c) -> decltype(std::begin(refTo(std::forward<C>(c))))
|
||||
{ return std::begin(refTo(std::forward<C>(c))); }
|
||||
template <typename C>
|
||||
static auto end(C &&c) -> decltype(std::end(refTo(std::forward<C>(c))))
|
||||
{ return std::end(refTo(std::forward<C>(c))); }
|
||||
template <typename C>
|
||||
static auto cbegin(C &&c) -> decltype(std::cbegin(refTo(std::forward<C>(c))))
|
||||
{ return std::cbegin(refTo(std::forward<C>(c))); }
|
||||
template <typename C>
|
||||
static auto cend(C &&c) -> decltype(std::cend(refTo(std::forward<C>(c))))
|
||||
{ return std::cend(refTo(std::forward<C>(c))); }
|
||||
template <typename C>
|
||||
static auto pos(C &&c, int i)
|
||||
{ return std::next(QGenericItemModelDetails::begin(std::forward<C>(c)), i); }
|
||||
template <typename C>
|
||||
static auto cpos(C &&c, int i)
|
||||
{ return std::next(QGenericItemModelDetails::cbegin(std::forward<C>(c)), i); }
|
||||
|
||||
// Test if a type is a range, and whether we can modify it using the
|
||||
// standard C++ container member functions insert, erase, and resize.
|
||||
@ -147,12 +237,13 @@ namespace QGenericItemModelDetails
|
||||
static constexpr bool has_resize = false;
|
||||
};
|
||||
template <typename C>
|
||||
struct range_traits<C, std::void_t<decltype(std::cbegin(std::declval<C&>())),
|
||||
decltype(std::cend(std::declval<C&>())),
|
||||
struct range_traits<C, std::void_t<decltype(cbegin(std::declval<C&>())),
|
||||
decltype(cend(std::declval<C&>())),
|
||||
std::enable_if_t<!is_multi_role_v<C>>
|
||||
>> : std::true_type
|
||||
{
|
||||
using value_type = std::remove_reference_t<decltype(*std::begin(std::declval<C&>()))>;
|
||||
using iterator = decltype(begin(std::declval<C&>()));
|
||||
using value_type = std::remove_reference_t<decltype(*std::declval<iterator&>())>;
|
||||
static constexpr bool is_mutable = !std::is_const_v<C> && !std::is_const_v<value_type>;
|
||||
static constexpr bool has_insert = test_insert<C>();
|
||||
static constexpr bool has_insert_range = test_insert_range<C>();
|
||||
@ -180,10 +271,6 @@ namespace QGenericItemModelDetails
|
||||
template <typename T> struct range_traits<const T *> : iterable_value<Mutable::No> {};
|
||||
template <> struct range_traits<QLatin1StringView> : iterable_value<Mutable::No> {};
|
||||
|
||||
template <typename T>
|
||||
using remove_ptr_and_ref_t =
|
||||
std::remove_pointer_t<std::remove_reference_t<decltype(refTo(std::declval<T&>()))>>;
|
||||
|
||||
template <typename C>
|
||||
[[maybe_unused]] static constexpr bool is_range_v = range_traits<C>();
|
||||
|
||||
@ -239,7 +326,7 @@ namespace QGenericItemModelDetails
|
||||
|
||||
template <typename T>
|
||||
[[maybe_unused]] static constexpr int static_size_v =
|
||||
row_traits<q20::remove_cvref_t<std::remove_pointer_t<T>>>::static_size;
|
||||
row_traits<std::remove_cv_t<wrapped_t<T>>>::static_size;
|
||||
|
||||
// tests for tree protocol implementation in the row type
|
||||
template <typename R, typename = void>
|
||||
@ -375,18 +462,18 @@ namespace QGenericItemModelDetails
|
||||
};
|
||||
|
||||
template <typename C, typename TreeProtocol = void,
|
||||
typename range_type = remove_ptr_and_ref_t<C>>
|
||||
typename range_type = wrapped_t<C>>
|
||||
using tree_protocol_t = typename tree_traits<
|
||||
range_type,
|
||||
std::remove_reference_t<decltype(*std::begin(std::declval<range_type&>()))>,
|
||||
TreeProtocol>::tree_protocol;
|
||||
|
||||
template <typename C, typename range_type = remove_ptr_and_ref_t<C>>
|
||||
template <typename C, typename range_type = wrapped_t<C>>
|
||||
using if_is_table_range = std::enable_if_t<
|
||||
is_range_v<range_type> && std::is_void_v<tree_protocol_t<range_type>>,
|
||||
bool>;
|
||||
|
||||
template <typename C, typename Protocol = void, typename range_type = remove_ptr_and_ref_t<C>>
|
||||
template <typename C, typename Protocol = void, typename range_type = wrapped_t<C>>
|
||||
using if_is_tree_range = std::enable_if_t<
|
||||
is_range_v<range_type> && !std::is_void_v<tree_protocol_t<range_type, Protocol>>,
|
||||
bool>;
|
||||
@ -427,7 +514,7 @@ protected:
|
||||
template <typename T, typename F>
|
||||
static auto for_element_at(T &&tuple, size_t idx, F &&function)
|
||||
{
|
||||
using type = std::remove_pointer_t<std::remove_reference_t<T>>;
|
||||
using type = QGenericItemModelDetails::wrapped_t<T>;
|
||||
constexpr size_t size = std::tuple_size_v<type>;
|
||||
Q_ASSERT(idx < size);
|
||||
return call_at(std::forward<T>(tuple), idx, std::make_index_sequence<size>{},
|
||||
@ -444,7 +531,7 @@ protected:
|
||||
template <typename T>
|
||||
static constexpr QMetaType meta_type_at(size_t idx)
|
||||
{
|
||||
using type = std::remove_pointer_t<std::remove_reference_t<T>>;
|
||||
using type = QGenericItemModelDetails::wrapped_t<T>;
|
||||
constexpr auto size = std::tuple_size_v<type>;
|
||||
Q_ASSERT(idx < size);
|
||||
return makeMetaTypes<type>(std::make_index_sequence<size>{}).at(idx);
|
||||
|
Loading…
x
Reference in New Issue
Block a user