From 9f63577dddfb01a9b78d6222b15489ee2320d82a Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Sun, 23 Mar 2025 09:58:29 +0100 Subject: [PATCH] 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 --- src/corelib/itemmodels/qgenericitemmodel.h | 6 +++--- src/corelib/itemmodels/qgenericitemmodel_impl.h | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/corelib/itemmodels/qgenericitemmodel.h b/src/corelib/itemmodels/qgenericitemmodel.h index 0f62bd966cd..90c5eadcb39 100644 --- a/src/corelib/itemmodels/qgenericitemmodel.h +++ b/src/corelib/itemmodels/qgenericitemmodel.h @@ -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 diff --git a/src/corelib/itemmodels/qgenericitemmodel_impl.h b/src/corelib/itemmodels/qgenericitemmodel_impl.h index a017f5c94e6..a3397957d29 100644 --- a/src/corelib/itemmodels/qgenericitemmodel_impl.h +++ b/src/corelib/itemmodels/qgenericitemmodel_impl.h @@ -178,6 +178,15 @@ namespace QGenericItemModelDetails template static auto pointerTo(T &t) { return std::addressof(t); } template static auto pointerTo(const T &&t) = delete; + template + static bool isValid(T &&t) + { + if constexpr (std::is_constructible_v) + return bool(t); + else + return true; + } + template auto key(It&& it) -> decltype(it.key()) { return it.key(); }