QGIM: test if a tuple row is valid before dereferencing

Row types that are convertible to bool, such as pointers, but also
unique_ptr and similar wrappers, should not be dereferenced when
accessing values for reading or writing. This is already done implicitly
when operating on single-value types, but for tuples we have to do so
explicitly, before accessing the element.

For example, inserting a row where the row type is a pointer will leave
that row be nullptr in the range, and both data and setData should fail
until a row has been constructed. For rows that are pointers to tuples,
we must never call get<>, as get cannot test if the item received is a
nullptr and still return a valid value (reference).

Since e.g. unique_ptr is explicitly convertible to bool we have to use
std::is_constructible rather than is_convertible, as the latter only
works for types that are implicitly convertible.

Change-Id: I9dbdf6e5cac0146ed5d1b88c523e182590b0c8ab
Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
This commit is contained in:
Volker Hilsheimer 2025-03-23 09:58:29 +01:00
parent 5be58ac927
commit 9f63577ddd
2 changed files with 12 additions and 3 deletions

View File

@ -277,7 +277,7 @@ public:
readData(*std::next(std::cbegin(row), index.column()));
else if constexpr (static_column_count == 0)
readData(row);
else
else if (QGenericItemModelDetails::isValid(row))
for_element_at(row, index.column(), readData);
}
return result;
@ -328,7 +328,7 @@ public:
success = writeData(*std::next(std::begin(row), index.column()));
} else if constexpr (static_column_count == 0) {
success = writeData(row);
} else {
} else if (QGenericItemModelDetails::isValid(row)) {
for_element_at(row, index.column(), [&writeData, &success](auto &&target){
using target_type = decltype(target);
// we can only assign to an lvalue reference
@ -361,7 +361,7 @@ public:
} else if constexpr (static_column_count == 0) {
row = row_type{};
success = true;
} else {
} else if (QGenericItemModelDetails::isValid(row)) {
for_element_at(row, index.column(), [&success](auto &&target){
using target_type = decltype(target);
if constexpr (std::is_lvalue_reference_v<target_type>

View File

@ -178,6 +178,15 @@ namespace QGenericItemModelDetails
template <typename T> static auto pointerTo(T &t) { return std::addressof(t); }
template <typename T> static auto pointerTo(const T &&t) = delete;
template <typename T>
static bool isValid(T &&t)
{
if constexpr (std::is_constructible_v<bool, T>)
return bool(t);
else
return true;
}
template <typename It>
auto key(It&& it) -> decltype(it.key()) { return it.key(); }