From eedbcad7a403cec40d939ba3f6bc7901fdf22c24 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 6 Jun 2024 11:27:33 +0200 Subject: [PATCH] QJniArray: allow container type for toContainer to be set explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a template parameter that is defaulted to what our logic would select as the appropritate container type. If the type is contiguous and the element is primitive, use the optimized implementation with the JNI helpers for copying the entire array region. Otherwise, use an emplace_back loop. Change-Id: I669bc3261f111bc0c7e3dd2af8fd33293c083801 Reviewed-by: Tor Arne Vestbø (cherry picked from commit a6b1f80cd6c833f6eb2fbfb778254cd7fcbc000f) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/kernel/qjniarray.h | 74 ++++++++++--------- src/corelib/kernel/qjniarray.qdoc | 16 +++- .../kernel/qjniarray/tst_qjniarray.cpp | 24 ++++++ 3 files changed, 77 insertions(+), 37 deletions(-) diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h index 41929c8e5fb..b1ebf3481ba 100644 --- a/src/corelib/kernel/qjniarray.h +++ b/src/corelib/kernel/qjniarray.h @@ -201,6 +201,13 @@ template 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 + using ToContainerType = typename ToContainerHelper::type; + public: using Type = T; @@ -306,7 +313,7 @@ public: else if constexpr (std::is_same_v) env->GetShortArrayRegion(object(), i, 1, &res); else if constexpr (std::is_same_v) - env->GetIntArrayRegion(object(), i, 1, &res); + env->GetIntArrayRegion(object(), i, 1, &res); else if constexpr (std::is_same_v) env->GetLongArrayRegion(object(), i, 1, &res); else if constexpr (std::is_same_v) @@ -317,60 +324,59 @@ public: } } - auto toContainer() const + template > + Container toContainer(Container &&container = {}) const { + const qsizetype sz = size(); + if (!sz) + return std::forward(container); JNIEnv *env = jniEnv(); - if constexpr (std::is_same_v) { - QList res; - res.reserve(size()); + + using ContainerType = q20::remove_cvref_t; + + container.reserve(sz); + if constexpr (std::is_same_v) { for (auto element : *this) - res.append(element); - return res; - } else if constexpr (std::is_same_v) { - QStringList res; - res.reserve(size()); + container.emplace_back(QJniObject(element).toString()); + } else if constexpr (std::is_base_of_v, std::remove_pointer_t>) { for (auto element : *this) - res.append(QJniObject(element).toString()); - return res; - } else if constexpr (std::is_same_v) { - const qsizetype bytecount = size(); - QByteArray res(bytecount, Qt::Initialization::Uninitialized); - if (!isEmpty()) { + container.emplace_back(element); + } else if constexpr (QJniArrayBase::isContiguousContainer) { + container.resize(sz); + if constexpr (std::is_same_v) { env->GetByteArrayRegion(object(), - 0, bytecount, reinterpret_cast(res.data())); - } - return res; - } else { - QList res; - if (isEmpty()) - return res; - res.resize(size()); - if constexpr (std::is_same_v) { + 0, sz, + reinterpret_cast(container.data())); + } else if constexpr (std::is_same_v) { env->GetCharArrayRegion(object(), - 0, res.size(), res.data()); + 0, sz, container.data()); } else if constexpr (std::is_same_v) { env->GetBooleanArrayRegion(object(), - 0, res.size(), res.data()); + 0, sz, container.data()); } else if constexpr (std::is_same_v) { env->GetShortArrayRegion(object(), - 0, res.size(), res.data()); + 0, sz, container.data()); } else if constexpr (std::is_same_v) { env->GetIntArrayRegion(object(), - 0, res.size(), res.data()); + 0, sz, container.data()); } else if constexpr (std::is_same_v) { env->GetLongArrayRegion(object(), - 0, res.size(), res.data()); + 0, sz, container.data()); } else if constexpr (std::is_same_v) { env->GetFloatArrayRegion(object(), - 0, res.size(), res.data()); + 0, sz, container.data()); } else if constexpr (std::is_same_v) { env->GetDoubleArrayRegion(object(), - 0, res.size(), res.data()); + 0, sz, container.data()); } else { - res.clear(); + static_assert(QtPrivate::type_dependent_false(), + "Don't know how to copy data from a QJniArray of this type"); } - return res; + } else { + for (auto e : *this) + container.emplace_back(e); } + return std::forward(container); } }; diff --git a/src/corelib/kernel/qjniarray.qdoc b/src/corelib/kernel/qjniarray.qdoc index 20c3fbe3ee1..c909d4650af 100644 --- a/src/corelib/kernel/qjniarray.qdoc +++ b/src/corelib/kernel/qjniarray.qdoc @@ -393,12 +393,22 @@ */ /*! - \fn template auto QJniArray::toContainer() const + \fn template template + Container QJniArray::toContainer(Container &&container) const - \return the data in the wrapped Java objects as a suitable C++ container, - which will be empty if the array is \l{QJniArrayBase::isValid()}{invalid}. + \return a container populated with the data in the wrapped Java array. + If no \a container is provided, then the type of the container returned + depends on the element type of this QJniArray. \include qjniarray.qdoc type-mapping + If you pass in a named container (an lvalue) for \a container, then that + container is filled, and a reference to it is returned. If you pass in a + temporary container (an rvalue, incl. the default argument), then that + container is filled, and returned by value. + + This function returns immediately if the array is + \l{QJniArrayBase::isValid()}{invalid}. + \sa QJniArrayBase::fromContainer() */ diff --git a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp index 2f895b7b9ed..f56b2fe2b16 100644 --- a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp +++ b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp @@ -20,6 +20,7 @@ private slots: void invalidArraysAreEmpty(); void size(); void operators(); + void toContainer(); }; using namespace QtJniTypes; @@ -80,6 +81,17 @@ VERIFY_RETURN_FOR_TYPE(List, List); VERIFY_RETURN_FOR_TYPE(List[], QJniArray); VERIFY_RETURN_FOR_TYPE(QJniArray, QJniArray); +#define VERIFY_CONTAINER_FOR_TYPE(In, Out) \ + static_assert(std::is_same_v().toContainer()), Out>) + +VERIFY_CONTAINER_FOR_TYPE(QJniArray, QList); +VERIFY_CONTAINER_FOR_TYPE(QJniArray, QList); +VERIFY_CONTAINER_FOR_TYPE(QJniArray, QByteArray); +VERIFY_CONTAINER_FOR_TYPE(QJniArray, QStringList); +VERIFY_CONTAINER_FOR_TYPE(QJniArray, QList); +VERIFY_CONTAINER_FOR_TYPE(QJniArray, QList); +VERIFY_CONTAINER_FOR_TYPE(QJniArray, QList); + void tst_QJniArray::construct() { // explicit @@ -209,6 +221,18 @@ void tst_QJniArray::operators() } } +void tst_QJniArray::toContainer() +{ + std::vector charVector{u'a', u'b', u'c'}; + QJniArray charArray(charVector); + + std::vector vector; + charArray.toContainer(vector); + + QCOMPARE(vector, charVector); + QCOMPARE(charArray.toContainer>(), charVector); +} + QTEST_MAIN(tst_QJniArray) #include "tst_qjniarray.moc"