diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h index 2cb5a98128a..f63433253da 100644 --- a/src/corelib/kernel/qjniarray.h +++ b/src/corelib/kernel/qjniarray.h @@ -151,6 +151,28 @@ private: class QJniArrayBase { // for SFINAE'ing out the fromContainer named constructor + template struct IsSequentialContainerHelper : std::false_type + { + static constexpr bool isForwardIterable = false; + }; + template + struct IsSequentialContainerHelper::iterator_category, + typename C::value_type, + decltype(std::size(std::declval())) + > + > : std::true_type + { + static constexpr bool isForwardIterable = std::is_convertible_v< + typename std::iterator_traits::iterator_category, + std::forward_iterator_tag + >; + }; + template <> + struct IsSequentialContainerHelper + { + static constexpr bool isForwardIterable = true; + }; + template struct IsContiguousContainerHelper : std::false_type {}; template struct IsContiguousContainerHelper())), @@ -161,6 +183,23 @@ class QJniArrayBase protected: // these are used in QJniArray + template + struct ElementTypeHelper + { + static constexpr bool isObject = false; + static constexpr bool isPrimitive = false; + }; + template + struct ElementTypeHelper> + { + using E = typename C::value_type; + static constexpr bool isObject = QtJniTypes::isObjectType(); + static constexpr bool isPrimitive = QtJniTypes::isPrimitiveType(); + }; + + template > + static constexpr bool isContiguousContainer = IsContiguousContainerHelper::value; + template using if_convertible = std::enable_if_t::value, bool>; template @@ -184,12 +223,21 @@ public: return 0; } - template - static constexpr bool isContiguousContainer = IsContiguousContainerHelper>::value; - template - using if_contiguous_container = std::enable_if_t, bool>; + // We can create an array from any forward-iterable container, and optimize + // for contiguous containers holding primitive elements. QJniArray is a + // forward-iterable container, so explicitly remove that from the overload + // set so that the copy constructors get used instead. + // Used also in the deduction guide, so must be public + template > + static constexpr bool isCompatibleSourceContainer = + (IsSequentialContainerHelper::isForwardIterable + || (isContiguousContainer && ElementTypeHelper::isPrimitive)) + && !std::is_base_of_v; - template = true> + template + using if_compatible_source_container = std::enable_if_t, bool>; + + template = true> static auto fromContainer(Container &&container) { Q_ASSERT_X(size_t(std::size(container)) <= size_t((std::numeric_limits::max)()), @@ -337,7 +385,7 @@ public: template = true> QJniArray(QJniArray &&other) noexcept = delete; - template = true> + template = true> explicit QJniArray(Container &&container) : QJniArrayBase(QJniArrayBase::fromContainer(std::forward(container))) { @@ -453,7 +501,7 @@ public: } else if constexpr (std::is_base_of_v, std::remove_pointer_t>) { for (auto element : *this) container.emplace_back(element); - } else if constexpr (QJniArrayBase::isContiguousContainer) { + } else if constexpr (isContiguousContainer) { container.resize(sz); if constexpr (QtJniTypes::sameTypeForJni) { env->GetByteArrayRegion(object(), @@ -496,7 +544,7 @@ public: // fromContainer() maps several C++ types to the same JNI type (e.g. both jboolean and // bool become QJniArray), we have to deduce to what fromContainer() would // give us. -template = true> +template = true> QJniArray(Container) -> QJniArray()))::value_type>; template @@ -508,10 +556,16 @@ auto QJniArrayBase::makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion) if (QJniEnvironment::checkAndClearExceptions(env)) return QJniArray(); - // can't use static_cast here because we have signed/unsigned mismatches if (length) { - (env->*setRegion)(localArray, 0, length, - reinterpret_cast(std::data(std::as_const(list)))); + // can't use static_cast here because we have signed/unsigned mismatches + if constexpr (isContiguousContainer) { + (env->*setRegion)(localArray, 0, length, + reinterpret_cast(std::data(std::as_const(list)))); + } else { + size_type i = 0; + for (const auto &e : std::as_const(list)) + (env->*setRegion)(localArray, i++, 1, reinterpret_cast(&e)); + } } return QJniArray(localArray); }; diff --git a/src/corelib/kernel/qjniarray.qdoc b/src/corelib/kernel/qjniarray.qdoc index 4bc38d6c22a..727f60e6497 100644 --- a/src/corelib/kernel/qjniarray.qdoc +++ b/src/corelib/kernel/qjniarray.qdoc @@ -16,15 +16,15 @@ */ /*! - \fn template > auto QJniArrayBase::fromContainer(Container &&container) + \fn template > auto QJniArrayBase::fromContainer(Container &&container) Creates a Java array holding the data in \a container, and returns a QJniArray instance that wraps it. -//! [contiguous-containers] +//! [forward-iterable-containers] This function only participates in overload resolution if \c{Container} - is a contiguous container storing elements of a \l{JNI types}{JNI type} - or equivalent C++ type. + is a container that stores elements of a \l{JNI types}{JNI type} or equivalent + C++ type, and provides a forward iterator. The specialization of the constructed QJniArray depends on the value type of the \a container. For a \c{Container} (such as e.g. \c{QList}) it @@ -47,7 +47,7 @@ \li QJniObject \li QJniArray \endtable -//! [contiguous-containers] +//! [forward-iterable-containers] \sa QJniArray::toContainer() */ @@ -278,13 +278,13 @@ */ /*! - \fn template template > QJniArray::QJniArray(Container &&container) + \fn template template > QJniArray::QJniArray(Container &&container) Constructs a QJniArray that wraps a newly-created Java array for elements of type \c{Container::value_type}, and populates the Java array with the data from \a container. - \include qjniarray.qdoc contiguous-containers + \include qjniarray.qdoc forward-iterable-containers \sa QJniArrayBase::fromContainer(), toContainer() */ diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h index e3168488fc7..c84effc160c 100644 --- a/src/corelib/kernel/qjniobject.h +++ b/src/corelib/kernel/qjniobject.h @@ -848,6 +848,16 @@ QT_END_NAMESPACE #include QT_BEGIN_NAMESPACE +namespace QtJniTypes { +namespace detail { +template +using FromContainerTest = decltype(QJniArrayBase::fromContainer(std::declval())); + +template +static constexpr bool isCompatibleSourceContainer = qxp::is_detected_v; +} +} + template template auto QJniObject::LocalFrame::convertToJni(T &&value) @@ -857,7 +867,7 @@ auto QJniObject::LocalFrame::convertToJni(T &&value) return newLocalRef(QJniObject::fromString(value)); } else if constexpr (QtJniTypes::IsJniArray::value) { return value.arrayObject(); - } else if constexpr (QJniArrayBase::isContiguousContainer) { + } else if constexpr (QtJniTypes::detail::isCompatibleSourceContainer) { using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::forward(value))); using ArrayType = decltype(std::declval().arrayObject()); return newLocalRef(QJniArrayBase::fromContainer(std::forward(value)).template object()); @@ -878,7 +888,7 @@ auto QJniObject::LocalFrame::convertFromJni(QJniObject &&object) return object.toString(); } else if constexpr (QtJniTypes::IsJniArray::value) { return T(std::move(object)); - } else if constexpr (QJniArrayBase::isContiguousContainer) { + } else if constexpr (QtJniTypes::detail::isCompatibleSourceContainer) { // if we were to create a QJniArray from Type... using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::declval())); // then that QJniArray would have elements of type diff --git a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp index 78d147a1660..61637abf972 100644 --- a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp +++ b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp @@ -182,6 +182,18 @@ void tst_QJniArray::construct() QJniArray list{QList{1, 2, 3}}; QCOMPARE(list.size(), 3); } + + // non-contiguous container + { + std::list list({1, 2, 3}); + QJniArray array(list); + QCOMPARE(array.size(), 3); + } + { + std::list list({QString(), QString(), QString()}); + QJniArray array(list); + QCOMPARE(array.size(), 3); + } } // Verify that we can convert QJniArrays into each other as long as element types