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"