From 5797326b160dd906b57834d09fb8ede9cca797bd Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Sun, 14 Jul 2024 16:27:01 +0200 Subject: [PATCH] JNI: Constrain QJniArray::toContainer to compatible target containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can populate a container with the contents of the Java array as long as the element types are convertible without narrowing (taking various special cases into account, such as jstring to QString). The container has to be either support emplace_back, or be a contiguous container if a primitive element type. The template helpers need to be in QJniArrayBase in order for qdoc to accept the input. Add test coverage, including static compile time tests. Change-Id: Id9372deed5cf33446ee1969dc284a88991db2aee Reviewed-by: Petri Virkkunen Reviewed-by: MÃ¥rten Nordheim (cherry picked from commit 8f04defa1e3973faec19a9cb1ab9bbf1ea7fb031) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/kernel/qjniarray.h | 43 +++++++++++++++---- src/corelib/kernel/qjniarray.qdoc | 2 +- .../kernel/qjniarray/tst_qjniarray.cpp | 27 ++++++++++++ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h index f63433253da..7dbd3d475d2 100644 --- a/src/corelib/kernel/qjniarray.h +++ b/src/corelib/kernel/qjniarray.h @@ -181,6 +181,13 @@ class QJniArrayBase > > : std::true_type {}; + template struct HasEmplaceBackTest : std::false_type {}; + template struct HasEmplaceBackTest().emplace_back(std::declval()))> + > : std::true_type + {}; + + protected: // these are used in QJniArray template @@ -205,6 +212,24 @@ protected: template using unless_convertible = std::enable_if_t::value, bool>; + // helpers for toContainer + template struct ToContainerHelper { using type = QList; }; + template <> struct ToContainerHelper { using type = QStringList; }; + template <> struct ToContainerHelper { using type = QByteArray; }; + template <> struct ToContainerHelper { using type = QByteArray; }; + + template + using ToContainerType = typename ToContainerHelper::type; + + template > + static constexpr bool isCompatibleTargetContainer = + (QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase::value + || QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase::value_type, + typename C::value_type>::value + || (std::is_base_of_v && std::is_same_v)) + && (qxp::is_detected_v + || (isContiguousContainer && ElementTypeHelper::isPrimitive)); + public: using size_type = jsize; using difference_type = size_type; @@ -236,6 +261,8 @@ public: template using if_compatible_source_container = std::enable_if_t, bool>; + template + using if_compatible_target_container = std::enable_if_t, bool>; template = true> static auto fromContainer(Container &&container) @@ -332,13 +359,10 @@ class QJniArray : public QJniArrayBase { friend struct QJniArrayIterator; - template struct ToContainerHelper { using type = QList; }; - template <> struct ToContainerHelper { using type = QStringList; }; - template <> struct ToContainerHelper { using type = QByteArray; }; - template <> struct ToContainerHelper { using type = QByteArray; }; - - template - using ToContainerType = typename ToContainerHelper::type; + template + using CanReserveTest = decltype(std::declval().reserve(0)); + template + static constexpr bool canReserve = qxp::is_detected_v; public: using Type = T; @@ -480,7 +504,7 @@ public: } } - template > + template , if_compatible_target_container = true> Container toContainer(Container &&container = {}) const { const qsizetype sz = size(); @@ -490,7 +514,8 @@ public: using ContainerType = q20::remove_cvref_t; - container.reserve(sz); + if constexpr (canReserve) + container.reserve(sz); if constexpr (std::is_same_v) { for (auto element : *this) { if constexpr (std::is_same_v) diff --git a/src/corelib/kernel/qjniarray.qdoc b/src/corelib/kernel/qjniarray.qdoc index 727f60e6497..633586a1db1 100644 --- a/src/corelib/kernel/qjniarray.qdoc +++ b/src/corelib/kernel/qjniarray.qdoc @@ -410,7 +410,7 @@ */ /*! - \fn template template Container QJniArray::toContainer(Container &&container) const + \fn template template > Container QJniArray::toContainer(Container &&container) const \return a container populated with the data in the wrapped Java array. diff --git a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp index 61637abf972..ead87713371 100644 --- a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp +++ b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp @@ -367,6 +367,29 @@ void tst_QJniArray::ordering() QCOMPARE_GT(arrayEnd, arrayBegin); } +template +using ToContainerTest = decltype(std::declval>().toContainer(std::declval())); + +template +static constexpr bool hasToContainer = qxp::is_detected_v; + +static_assert(hasToContainer>); +static_assert(hasToContainer>); + +static_assert(hasToContainer); +static_assert(hasToContainer); +static_assert(hasToContainer); +static_assert(hasToContainer>); +static_assert(hasToContainer>); +// different types but doesn't narrow +static_assert(hasToContainer>); +static_assert(hasToContainer>); +// would narrow +static_assert(!hasToContainer>); +static_assert(!hasToContainer>); +// incompatible types +static_assert(!hasToContainer); + void tst_QJniArray::toContainer() { std::vector charVector{u'a', u'b', u'c'}; @@ -377,6 +400,10 @@ void tst_QJniArray::toContainer() QCOMPARE(vector, charVector); QCOMPARE(charArray.toContainer>(), charVector); + + // non-contiguous container of primitive elements + std::list charList = charArray.toContainer>(); + QCOMPARE(charList.size(), charVector.size()); } void tst_QJniArray::pointerToValue()