summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qjniarray.h
blob: a69b4a6dac353af874467e1224b8316ef3a93664 (plain)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
// Copyright (C) 2023 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#ifndef QJNIARRAY_H#define QJNIARRAY_H#include <QtCore/qlist.h>#if defined(Q_QDOC) || defined(Q_OS_ANDROID)#include <QtCore/qbytearray.h>#include <QtCore/qjniobject.h>#include <iterator>#include <QtCore/q26numeric.h>#include <QtCore/q20type_traits.h>#include <QtCore/q20utility.h>#if defined(Q_QDOC)using jsize = qint32;using jarray = jobject;#endif QT_BEGIN_NAMESPACE template<typename T>class QJniArray;template<typename T>struct QJniArrayMutableIterator;// forward declare here so that we don't have to include the private headernamespace QtAndroidPrivate { Q_CORE_EXPORT jclass findClass(const char*className, JNIEnv *env);}template<typename T>struct QJniArrayIterator {private:using VT =std::remove_const_t<T>;friend class QJniArray<VT>;friendstruct QJniArrayMutableIterator<VT>;// Since QJniArray doesn't hold values, we need a wrapper to be able to hand// out a pointer to a value.struct QJniArrayValueRef { T ref;const T *operator->()const{return&ref; }};public:QJniArrayIterator() =default;constexprQJniArrayIterator(const QJniArrayMutableIterator<VT> &other) noexcept :m_index(other.m_index),m_array(other.m_array){}constexprQJniArrayIterator(QJniArrayMutableIterator<VT> &&other) noexcept :m_index(std::exchange(other.m_index, -1)),m_array(std::exchange(other.m_array,nullptr)){}constexprQJniArrayIterator(const QJniArrayIterator &other) noexcept =default;constexprQJniArrayIterator(QJniArrayIterator &&other) noexcept =default;constexpr QJniArrayIterator &operator=(const QJniArrayIterator &other) noexcept =default;constexpr QJniArrayIterator &operator=(QJniArrayIterator &&other) noexcept =default;using difference_type = jsize;using value_type = T;using pointer = QJniArrayValueRef;using reference = T;// difference to container requirementsusing const_reference = reference;using iterator_category =std::random_access_iterator_tag; const_reference operator*()const{return m_array->at(m_index);} QJniArrayValueRef operator->()const{return{m_array->at(m_index)};} const_reference operator[](difference_type n)const{return m_array->at(m_index + n);}friend QJniArrayIterator &operator++(QJniArrayIterator &that) noexcept {++that.m_index;return that;}friend QJniArrayIterator operator++(QJniArrayIterator &that,int) noexcept {auto copy = that;++that;return copy;}friend QJniArrayIterator operator+(const QJniArrayIterator &that, difference_type n) noexcept {return{that.m_index + n, that.m_array};}friend QJniArrayIterator operator+(difference_type n,const QJniArrayIterator &that) noexcept {return that + n;}friend QJniArrayIterator &operator+=(QJniArrayIterator &that, difference_type n) noexcept { that.m_index += n;return that;}friend QJniArrayIterator &operator--(QJniArrayIterator &that) noexcept {--that.m_index;return that;}friend QJniArrayIterator operator--(QJniArrayIterator &that,int) noexcept {auto copy = that;--that;return copy;}friend QJniArrayIterator operator-(const QJniArrayIterator &that, difference_type n) noexcept {return{that.m_index - n, that.m_array};}friend QJniArrayIterator operator-(difference_type n,const QJniArrayIterator &that) noexcept {return{n - that.m_index, that.m_array};}friend QJniArrayIterator &operator-=(QJniArrayIterator &that, difference_type n) noexcept { that.m_index -= n;return that;}friend difference_type operator-(const QJniArrayIterator &lhs,const QJniArrayIterator &rhs){Q_ASSERT(lhs.m_array == rhs.m_array);return lhs.m_index - rhs.m_index;}voidswap(QJniArrayIterator &other) noexcept {std::swap(m_index, other.m_index);qt_ptr_swap(m_array, other.m_array);}private:friend constexprboolcomparesEqual(const QJniArrayIterator &lhs,const QJniArrayIterator &rhs) noexcept {Q_ASSERT(lhs.m_array == rhs.m_array);return lhs.m_index == rhs.m_index;}friend constexpr Qt::strong_ordering compareThreeWay(const QJniArrayIterator &lhs,const QJniArrayIterator &rhs) noexcept {Q_ASSERT(lhs.m_array == rhs.m_array);returnQt::compareThreeWay(lhs.m_index, rhs.m_index);}Q_DECLARE_STRONGLY_ORDERED(QJniArrayIterator) qsizetype m_index =0;const QJniArray<VT> *m_array =nullptr;QJniArrayIterator(qsizetype index,const QJniArray<VT> *array):m_index(index),m_array(array){}};template<typename T>// need to specialize traits for it, so can't be nestedstruct QJniArrayMutableValueRef;template<typename T>struct QJniArrayMutableIterator {private:friendstruct QJniArrayIterator<const T>;friendstruct QJniArrayMutableValueRef<T>;public:constexprQJniArrayMutableIterator() noexcept =default;constexprQJniArrayMutableIterator(const QJniArrayIterator<const T> &other) noexcept :m_index(other.m_index),m_array(other.m_array){}constexprQJniArrayMutableIterator(QJniArrayIterator<const T> &&other) noexcept :m_index(std::exchange(other.m_index, -1)),m_array(std::exchange(other.m_array,nullptr)){}constexprQJniArrayMutableIterator(const QJniArrayMutableIterator &other) noexcept =default;constexprQJniArrayMutableIterator(QJniArrayMutableIterator &&other) noexcept =default;constexpr QJniArrayMutableIterator &operator=(const QJniArrayMutableIterator &other) noexcept =default;constexpr QJniArrayMutableIterator &operator=(QJniArrayMutableIterator &&other) noexcept =default;using difference_type = jsize;using value_type = T;using pointer = QJniArrayMutableValueRef<T>;using reference = QJniArrayMutableValueRef<T>;// difference to container requirementsusing const_reference = T;using iterator_category =std::random_access_iterator_tag; const_reference operator*()const{return m_array->at(m_index);} reference operator*(){return{m_array->at(m_index), *this};}const pointer operator->()const{return{m_array->at(m_index)};} pointer operator->(){return{m_array->at(m_index), *this};} const_reference operator[](difference_type n)const{return m_array->at(m_index + n);} reference operator[](difference_type n){return{m_array->at(m_index + n), *this};}friend QJniArrayMutableIterator &operator++(QJniArrayMutableIterator &that) noexcept {++that.m_index;return that;}friend QJniArrayMutableIterator operator++(QJniArrayMutableIterator &that,int) noexcept {auto copy = that;++that;return copy;}friend QJniArrayMutableIterator operator+(const QJniArrayMutableIterator &that, difference_type n) noexcept {return{that.m_index + n, that.m_array};}friend QJniArrayMutableIterator operator+(difference_type n,const QJniArrayMutableIterator &that) noexcept {return that + n;}friend QJniArrayMutableIterator &operator+=(QJniArrayMutableIterator &that, difference_type n) noexcept { that.m_index += n;return that;}friend QJniArrayMutableIterator &operator--(QJniArrayMutableIterator &that) noexcept {--that.m_index;return that;}friend QJniArrayMutableIterator operator--(QJniArrayMutableIterator &that,int) noexcept {auto copy = that;--that;return copy;}friend QJniArrayMutableIterator operator-(const QJniArrayMutableIterator &that, difference_type n) noexcept {return{that.m_index - n, that.m_array};}friend QJniArrayMutableIterator operator-(difference_type n,const QJniArrayMutableIterator &that) noexcept {return{n - that.m_index, that.m_array};}friend QJniArrayMutableIterator &operator-=(QJniArrayMutableIterator &that, difference_type n) noexcept { that.m_index -= n;return that;}friend difference_type operator-(const QJniArrayMutableIterator &lhs,const QJniArrayMutableIterator &rhs){Q_ASSERT(lhs.m_array == rhs.m_array);return lhs.m_index - rhs.m_index;}voidswap(QJniArrayMutableIterator &other) noexcept {std::swap(m_index, other.m_index);qt_ptr_swap(m_array, other.m_array);}private:friend constexprboolcomparesEqual(const QJniArrayMutableIterator &lhs,const QJniArrayMutableIterator &rhs){Q_ASSERT(lhs.m_array == rhs.m_array);return lhs.m_index == rhs.m_index;}friend constexprboolcomparesEqual(const QJniArrayMutableIterator &lhs,const QJniArrayIterator<const T> &rhs){Q_ASSERT(lhs.m_array == rhs.m_array);return lhs.m_index == rhs.m_index;}friend constexpr Qt::strong_ordering compareThreeWay(const QJniArrayMutableIterator &lhs,const QJniArrayMutableIterator &rhs){Q_ASSERT(lhs.m_array == rhs.m_array);returnQt::compareThreeWay(lhs.m_index, rhs.m_index);}friend constexpr Qt::strong_ordering compareThreeWay(const QJniArrayMutableIterator &lhs,const QJniArrayIterator<const T> &rhs){Q_ASSERT(lhs.m_array == rhs.m_array);returnQt::compareThreeWay(lhs.m_index, rhs.m_index);}Q_DECLARE_STRONGLY_ORDERED_NON_NOEXCEPT(QJniArrayMutableIterator)Q_DECLARE_STRONGLY_ORDERED_NON_NOEXCEPT(QJniArrayMutableIterator, QJniArrayIterator<const T>)using VT =std::remove_const_t<T>;friend class QJniArray<VT>; qsizetype m_index =0; QJniArray<VT> *m_array =nullptr;QJniArrayMutableIterator(qsizetype index, QJniArray<VT> *array):m_index(index),m_array(array){}};template<typename T>// need to specialize traits for it, so can't be nestedstruct QJniArrayMutableValueRef { T value; QJniArrayMutableIterator<T> back = {-1,nullptr};operatorT()const{return value; }const T &operator*()const{return value; } T &operator*() {return value; }const T *operator->()const{return&value; } T *operator->() =delete;// no write-back, so delete explicitly QJniArrayMutableValueRef &operator=(const QJniArrayMutableValueRef &other){return*this= *other;} QJniArrayMutableValueRef &operator=(QJniArrayMutableValueRef &&other){return*this=std::move(*other);} QJniArrayMutableValueRef &operator=(const T &v){Q_ASSERT(back.m_array); value = v; back.m_array->setValue(back.m_index, value);return*this;} QJniArrayMutableValueRef &operator=(T &&v){Q_ASSERT(back.m_array); value =std::move(v); back.m_array->setValue(back.m_index, value);return*this;}};class QJniArrayBase {// for SFINAE'ing out the fromContainer named constructortemplate<typename C, typename =void>struct IsSequentialContainerHelper :std::false_type {staticconstexprbool isForwardIterable =false;};template<typename C>struct IsSequentialContainerHelper<C,std::void_t<typename std::iterator_traits<typename C::const_iterator>::iterator_category, typename C::value_type,decltype(std::size(std::declval<C>()))>> :std::true_type {staticconstexprbool isForwardIterable =std::is_convertible_v< typename std::iterator_traits<typename C::const_iterator>::iterator_category,std::forward_iterator_tag >;};template<>struct IsSequentialContainerHelper<QByteArray,void>{staticconstexprbool isForwardIterable =true;};template<typename C, typename =void>struct IsContiguousContainerHelper :std::false_type {};template<typename C>struct IsContiguousContainerHelper<C,std::void_t<decltype(std::data(std::declval<C>())),decltype(std::size(std::declval<C>())), typename C::value_type >> :std::true_type {};template<typename C, typename =void>struct HasEmplaceBackTest :std::false_type {};template<typename C>struct HasEmplaceBackTest<C,std::void_t<decltype(std::declval<C>().emplace_back(std::declval<typename C::value_type>()))>> :std::true_type {};protected:// these are used in QJniArraytemplate<typename C, typename =void>struct ElementTypeHelper {staticconstexprbool isObject =false;staticconstexprbool isPrimitive =false;};template<typename C>struct ElementTypeHelper<C,std::void_t<typename C::value_type>>{using E = typename C::value_type;staticconstexprbool isObject =QtJniTypes::isObjectType<E>();staticconstexprbool isPrimitive =QtJniTypes::isPrimitiveType<E>();};template<typename CRef, typename C =q20::remove_cvref_t<CRef>>staticconstexprbool isContiguousContainer = IsContiguousContainerHelper<C>::value;template<typename From, typename To>using if_convertible =std::enable_if_t<QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase<From, To>::value,bool>;template<typename From, typename To>using unless_convertible =std::enable_if_t<!QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase<From, To>::value,bool>;// helpers for toContainertemplate<typename E>struct ToContainerHelper {using type = QList<E>; };template<>struct ToContainerHelper<jstring> {using type = QStringList; };template<>struct ToContainerHelper<jbyte> {using type = QByteArray; };template<>struct ToContainerHelper<char> {using type = QByteArray; };template<typename E>using ToContainerType = typename ToContainerHelper<E>::type;template<typename E, typename CRef, typename C =q20::remove_cvref_t<CRef>>staticconstexprbool isCompatibleTargetContainer =(QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase<E, typename C::value_type>::value ||QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase<typename ToContainerType<E>::value_type, typename C::value_type>::value || (std::is_base_of_v<QtJniTypes::JObjectBase, E> &&std::is_same_v<typename C::value_type, QString>))&& (qxp::is_detected_v<HasEmplaceBackTest, C>|| (isContiguousContainer<C> && ElementTypeHelper<C>::isPrimitive));public:using size_type = jsize;using difference_type = size_type;operatorQJniObject()const{return m_object; }template<typename T = jobject> T object()const{return m_object.object<T>(); }boolisValid()const{return m_object.isValid(); }boolisEmpty()const{returnsize() ==0; } size_type size()const{if(jarray array = m_object.object<jarray>())returnjniEnv()->GetArrayLength(array);return0;}// 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 publictemplate<typename C>using IsSequentialOrContiguous =std::bool_constant< IsSequentialContainerHelper<C>::isForwardIterable || (isContiguousContainer<C> && ElementTypeHelper<C>::isPrimitive)>;template<typename CRef, typename C =q20::remove_cvref_t<CRef>>staticconstexprbool isCompatibleSourceContainer =std::conjunction_v<std::negation<std::is_same<QString, C>>, IsSequentialOrContiguous<C>,std::negation<std::is_base_of<QJniArrayBase, C>>>;template<typename C>using if_compatible_source_container =std::enable_if_t<isCompatibleSourceContainer<C>,bool>;template<typename T, typename C>using if_compatible_target_container =std::enable_if_t<isCompatibleTargetContainer<T, C>,bool>;template<typename Container, if_compatible_source_container<Container> =true>staticautofromContainer(Container &&container){verifySize(std::size(container));using ElementType = typename std::remove_reference_t<Container>::value_type;ifconstexpr(std::is_base_of_v<std::remove_pointer_t<jobject>,std::remove_pointer_t<ElementType>>) {returnmakeObjectArray(std::forward<Container>(container));}else ifconstexpr(std::disjunction_v<std::is_same<ElementType, QJniObject>,std::is_same<ElementType, QString>,std::is_base_of<QtJniTypes::JObjectBase, ElementType>>) {return QJniArray<ElementType>(makeObjectArray(std::forward<Container>(container)).arrayObject());}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jfloat>) {return makeArray<jfloat>(std::forward<Container>(container), &JNIEnv::NewFloatArray,&JNIEnv::SetFloatArrayRegion);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jdouble>) {return makeArray<jdouble>(std::forward<Container>(container), &JNIEnv::NewDoubleArray,&JNIEnv::SetDoubleArrayRegion);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jboolean>) {return makeArray<jboolean>(std::forward<Container>(container), &JNIEnv::NewBooleanArray,&JNIEnv::SetBooleanArrayRegion);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jbyte>||std::is_same_v<ElementType,char>) {return makeArray<jbyte>(std::forward<Container>(container), &JNIEnv::NewByteArray,&JNIEnv::SetByteArrayRegion);}else ifconstexpr(std::disjunction_v<std::is_same<ElementType, jchar>,std::is_same<ElementType, QChar>>) {return makeArray<jchar>(std::forward<Container>(container), &JNIEnv::NewCharArray,&JNIEnv::SetCharArrayRegion);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jshort>) {return makeArray<jshort>(std::forward<Container>(container), &JNIEnv::NewShortArray,&JNIEnv::SetShortArrayRegion);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jint>) {return makeArray<jint>(std::forward<Container>(container), &JNIEnv::NewIntArray,&JNIEnv::SetIntArrayRegion);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jlong>) {return makeArray<jlong>(std::forward<Container>(container), &JNIEnv::NewLongArray,&JNIEnv::SetLongArrayRegion);}else{static_assert(QtPrivate::type_dependent_false<ElementType>(),"Don't know how to make QJniArray for this element type");}}protected:QJniArrayBase() =default;~QJniArrayBase() =default;explicitQJniArrayBase(const QJniArrayBase &other) =default;explicitQJniArrayBase(QJniArrayBase &&other) noexcept =default; QJniArrayBase &operator=(const QJniArrayBase &other) =default; QJniArrayBase &operator=(QJniArrayBase &&other) noexcept =default;explicitQJniArrayBase(jarray array):m_object(static_cast<jobject>(array)){}explicitQJniArrayBase(const QJniObject &object):m_object(object){}explicitQJniArrayBase(QJniObject &&object) noexcept :m_object(std::move(object)){} QJniArrayBase &operator=(const QJniObject &object){ m_object = object;return*this;} QJniArrayBase &operator=(QJniObject &&object) noexcept { m_object =std::move(object);return*this;} JNIEnv *jniEnv()const noexcept {returnQJniEnvironment::getJniEnv(); }template<typename ElementType, typename List, typename NewFn, typename SetFn>staticautomakeArray(List &&list, NewFn &&newArray, SetFn &&setRegion);template<typename List>staticautomakeObjectArray(List &&list);template<typename ElementType>staticautomakeEmptyArray(size_type size){auto env =QJniEnvironment();ifconstexpr(std::disjunction_v<std::is_base_of<std::remove_pointer_t<jobject>,std::remove_pointer_t<ElementType>>,std::is_same<ElementType, QJniObject>,std::is_same<ElementType, QString>,std::is_base_of<QtJniTypes::JObjectBase, ElementType>>) {using ResultType =decltype(QtJniTypes::Traits<ElementType>::convertToJni(nullptr, {}));constauto className =QtJniTypes::Traits<ResultType>::className(); jclass elementClass = env.findClass(className);if(!elementClass) { env.checkAndClearExceptions();returnjobjectArray(nullptr);}return env->NewObjectArray(size, elementClass,nullptr);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jfloat>) {return env->NewFloatArray(size);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jdouble>) {return env->NewDoubleArray(size);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jboolean>) {return env->NewBooleanArray(size);}else ifconstexpr(std::disjunction_v<std::is_same<ElementType, jbyte>,std::is_same<ElementType,char>>) {return env->NewByteArray(size);}else ifconstexpr(std::disjunction_v<std::is_same<ElementType, jchar>,std::is_same<ElementType, QChar>>) {return env->NewCharArray(size);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jshort>) {return env->NewShortArray(size);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jint>) {return env->NewIntArray(size);}else ifconstexpr(QtJniTypes::sameTypeForJni<ElementType, jlong>) {return env->NewLongArray(size);}else{static_assert(QtPrivate::type_dependent_false<ElementType>(),"Don't know how to make QJniArray for this element type");}}voidswap(QJniArrayBase &other) noexcept { m_object.swap(other.m_object); }private:template<typename container_size_type>static voidverifySize(container_size_type size){if(!q20::in_range<size_type>(size))qWarning("QJniArray::fromContainer: Container is too large for Java and will be truncated!");} QJniObject m_object;};template<typename T>class QJniArray :public QJniArrayBase {friendstruct QJniArrayIterator<T>;template<typename C>using CanReserveTest =decltype(std::declval<C>().reserve(0));template<typename C>staticconstexprbool canReserve =qxp::is_detected_v<CanReserveTest, C>;public:using Type = T;using value_type = T;using iterator = QJniArrayMutableIterator<T>;using reverse_iterator =std::reverse_iterator<iterator>;using const_iterator = QJniArrayIterator<const T>;using const_reverse_iterator =std::reverse_iterator<const_iterator>;using reference = typename iterator::reference;using const_reference = typename const_iterator::const_reference;QJniArray() =default;explicitQJniArray(jarray array) :QJniArrayBase(array) {}explicitQJniArray(const QJniObject &object) :QJniArrayBase(object) {}explicitQJniArray(QJniObject &&object) noexcept :QJniArrayBase(std::move(object)) {}template<typename Other, if_convertible<Other, T> =true>QJniArray(const QJniArray<Other> &other):QJniArrayBase(other){}template<typename Other, if_convertible<Other, T> =true>QJniArray(QJniArray<Other> &&other) noexcept :QJniArrayBase(std::move(other)){}template<typename Other, if_convertible<Other, T> =true> QJniArray &operator=(const QJniArray<Other> &other){QJniArrayBase::operator=(QJniObject(other));return*this;}template<typename Other, if_convertible<Other, T> =true> QJniArray &operator=(QJniArray<Other> &&other) noexcept { QJniArray moved(std::move(other));swap(moved);return*this;}// explicitly delete to disable detour via operator QJniObject()template<typename Other, unless_convertible<Other, T> =true>QJniArray(const QJniArray<Other> &other) =delete;template<typename Other, unless_convertible<Other, T> =true>QJniArray(QJniArray<Other> &&other) noexcept =delete;template<typename Container, if_compatible_source_container<Container> =true>explicitQJniArray(Container &&container):QJniArrayBase(QJniArrayBase::fromContainer(std::forward<Container>(container))){} Q_IMPLICIT inlineQJniArray(std::initializer_list<T> list):QJniArrayBase(QJniArrayBase::fromContainer(list)){}explicitQJniArray(size_type size):QJniArrayBase(makeEmptyArray<T>(size)){}~QJniArray() =default;autoarrayObject()const{ifconstexpr(QtJniTypes::isObjectType<T>())return object<jobjectArray>();else ifconstexpr(QtJniTypes::sameTypeForJni<T, jbyte>)return object<jbyteArray>();else ifconstexpr(QtJniTypes::sameTypeForJni<T, jchar>)return object<jcharArray>();else ifconstexpr(QtJniTypes::sameTypeForJni<T, jboolean>)return object<jbooleanArray>();else ifconstexpr(QtJniTypes::sameTypeForJni<T, jshort>)return object<jshortArray>();else ifconstexpr(QtJniTypes::sameTypeForJni<T, jint>)return object<jintArray>();else ifconstexpr(QtJniTypes::sameTypeForJni<T, jlong>)return object<jlongArray>();else ifconstexpr(QtJniTypes::sameTypeForJni<T, jfloat>)return object<jfloatArray>();else ifconstexpr(QtJniTypes::sameTypeForJni<T, jdouble>)return object<jdoubleArray>();elsereturn object<jarray>();} const_iterator begin()const noexcept {return{0,this}; } const_iterator constBegin()const noexcept {returnbegin(); } const_iterator cbegin()const noexcept {returnbegin(); } const_iterator end()const noexcept {return{size(),this}; } const_iterator constEnd()const noexcept {return{end()}; } const_iterator cend()const noexcept {return{end()}; } iterator begin() noexcept {return{0,this}; } iterator end() noexcept {return{size(),this}; } const_reverse_iterator rbegin()const noexcept {returnconst_reverse_iterator(end()); } const_reverse_iterator rend()const noexcept {returnconst_reverse_iterator(begin()); } const_reverse_iterator crbegin()const noexcept {returnconst_reverse_iterator(end()); } const_reverse_iterator crend()const noexcept {returnconst_reverse_iterator(begin()); } reverse_iterator rbegin() noexcept {returnreverse_iterator(end()); } reverse_iterator rend() noexcept {returnreverse_iterator(begin()); } const_reference operator[](size_type i)const{returnat(i); } reference operator[](size_type i) {return reference{at(i), iterator{i,this}}; } const_reference at(size_type i)const{ JNIEnv *env =jniEnv();ifconstexpr(QtJniTypes::isObjectType<T>()) { jobject element = env->GetObjectArrayElement(object<jobjectArray>(), i);ifconstexpr(std::is_base_of_v<std::remove_pointer_t<jobject>,std::remove_pointer_t<T>>)return static_cast<T>(element);elsereturnQtJniTypes::Traits<T>::convertFromJni(QJniObject::fromLocalRef(element));}else{ T res = {};ifconstexpr(QtJniTypes::sameTypeForJni<T, jbyte>) env->GetByteArrayRegion(object<jbyteArray>(), i,1, &res);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jchar>) env->GetCharArrayRegion(object<jcharArray>(), i,1, &res);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jboolean>) env->GetBooleanArrayRegion(object<jbooleanArray>(), i,1, &res);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jshort>) env->GetShortArrayRegion(object<jshortArray>(), i,1, &res);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jint>) env->GetIntArrayRegion(object<jintArray>(), i,1, &res);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jlong>) env->GetLongArrayRegion(object<jlongArray>(), i,1, &res);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jfloat>) env->GetFloatArrayRegion(object<jfloatArray>(), i,1, &res);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jdouble>) env->GetDoubleArrayRegion(object<jdoubleArray>(), i,1, &res);return res;}}voidsetValue(size_type i, const_reference &val){ JNIEnv *env =jniEnv();ifconstexpr(QtJniTypes::isObjectType<T>()) {QtJniTypes::Detail::LocalFrame<T>frame(env); jobject element = frame.templateconvertToJni(val); env->SetObjectArrayElement(object<jobjectArray>(), i, element);}else{// primitive typesifconstexpr(QtJniTypes::sameTypeForJni<T, jbyte>) env->SetByteArrayRegion(object<jbyteArray>(), i,1, &val);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jchar>) env->SetCharArrayRegion(object<jcharArray>(), i,1, &val);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jboolean>) env->SetBooleanArrayRegion(object<jbooleanArray>(), i,1, &val);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jshort>) env->SetShortArrayRegion(object<jshortArray>(), i,1, &val);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jint>) env->SetIntArrayRegion(object<jintArray>(), i,1, &val);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jlong>) env->SetLongArrayRegion(object<jlongArray>(), i,1, &val);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jfloat>) env->SetFloatArrayRegion(object<jfloatArray>(), i,1, &val);else ifconstexpr(QtJniTypes::sameTypeForJni<T, jdouble>) env->SetDoubleArrayRegion(object<jdoubleArray>(), i,1, &val);}}template<typename Container = ToContainerType<T>, if_compatible_target_container<T, Container> =true> Container toContainer(Container &&container = {})const{const qsizetype sz =size();if(!sz)returnstd::forward<Container>(container); JNIEnv *env =jniEnv();using ContainerType =q20::remove_cvref_t<Container>;ifconstexpr(canReserve<ContainerType>) container.reserve(sz);ifconstexpr(std::is_same_v<typename ContainerType::value_type, QString>) {for(auto element : *this) {ifconstexpr(std::is_same_v<decltype(element), QString>) { container.emplace_back(element);}else ifconstexpr(std::is_same_v<decltype(element), jstring>) { container.emplace_back(element ?QtJniTypes::Detail::toQString(element, env): QString{});}else{ container.emplace_back(QJniObject(element).toString());}}}else ifconstexpr(std::is_base_of_v<std::remove_pointer_t<jobject>,std::remove_pointer_t<T>>) {for(auto element : *this) container.emplace_back(element);}else ifconstexpr(isContiguousContainer<ContainerType>) { container.resize(sz);ifconstexpr(QtJniTypes::sameTypeForJni<T, jbyte>) { env->GetByteArrayRegion(object<jbyteArray>(),0, sz,reinterpret_cast<jbyte *>(container.data()));}else ifconstexpr(QtJniTypes::sameTypeForJni<T, jchar>) { env->GetCharArrayRegion(object<jcharArray>(),0, sz, container.data());}else ifconstexpr(QtJniTypes::sameTypeForJni<T, jboolean>) { env->GetBooleanArrayRegion(object<jbooleanArray>(),0, sz, container.data());}else ifconstexpr(QtJniTypes::sameTypeForJni<T, jshort>) { env->GetShortArrayRegion(object<jshortArray>(),0, sz, container.data());}else ifconstexpr(QtJniTypes::sameTypeForJni<T, jint>) { env->GetIntArrayRegion(object<jintArray>(),0, sz, container.data());}else ifconstexpr(QtJniTypes::sameTypeForJni<T, jlong>) { env->GetLongArrayRegion(object<jlongArray>(),0, sz, container.data());}else ifconstexpr(QtJniTypes::sameTypeForJni<T, jfloat>) { env->GetFloatArrayRegion(object<jfloatArray>(),0, sz, container.data());}else ifconstexpr(QtJniTypes::sameTypeForJni<T, jdouble>) { env->GetDoubleArrayRegion(object<jdoubleArray>(),0, sz, container.data());}else{static_assert(QtPrivate::type_dependent_false<T>(),"Don't know how to copy data from a QJniArray of this type");}}else{for(auto e : *this) container.emplace_back(e);}returnstd::forward<Container>(container);}};// Deduction guide so that we can construct as 'QJniArray list(Container<T>)'. Since// fromContainer() maps several C++ types to the same JNI type (e.g. both jboolean and// bool become QJniArray<jboolean>), we have to deduce to what fromContainer() would// give us.template<typename Container,QJniArrayBase::if_compatible_source_container<Container> =true>QJniArray(Container) -> QJniArray<typename decltype(QJniArrayBase::fromContainer(std::declval<Container>()))::value_type>;template<typename ElementType, typename List, typename NewFn, typename SetFn>auto QJniArrayBase::makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion){const size_type length =size_type(std::size(list)); JNIEnv *env =QJniEnvironment::getJniEnv();auto localArray = (env->*newArray)(length);if(QJniEnvironment::checkAndClearExceptions(env)) {if(localArray) env->DeleteLocalRef(localArray);return QJniArray<ElementType>();}if(length) {// can't use static_cast here because we have signed/unsigned mismatchesifconstexpr(isContiguousContainer<List>) {(env->*setRegion)(localArray,0, length,reinterpret_cast<const ElementType *>(std::data(std::as_const(list))));}else{ size_type i =0;for(constauto&e :std::as_const(list))(env->*setRegion)(localArray, i++,1,reinterpret_cast<const ElementType *>(&e));}}return QJniArray<ElementType>(QJniObject::fromLocalRef(localArray));};template<typename List>auto QJniArrayBase::makeObjectArray(List &&list){using ElementType = typename q20::remove_cvref_t<List>::value_type;using ResultType = QJniArray<decltype(QtJniTypes::Traits<ElementType>::convertToJni(nullptr,{}))>;if(std::size(list) ==0)returnResultType(); JNIEnv *env =QJniEnvironment::getJniEnv();const size_type length =q26::saturate_cast<size_type>(std::size(list));// this assumes that all objects in the list have the same class jclass elementClass =nullptr;ifconstexpr(std::disjunction_v<std::is_same<ElementType, QJniObject>,std::is_base_of<QtJniTypes::JObjectBase, ElementType>>) { elementClass =std::begin(list)->objectClass();}else ifconstexpr(std::is_same_v<ElementType, QString>) { elementClass =QtAndroidPrivate::findClass("java/lang/String", env);}else{ elementClass = env->GetObjectClass(*std::begin(list));}auto localArray = env->NewObjectArray(length, elementClass,nullptr);if(QJniEnvironment::checkAndClearExceptions(env)) {if(localArray) env->DeleteLocalRef(localArray);returnResultType();}// explicitly manage the frame for local references in chunks of 100constexpr jint frameCapacity =100; qsizetype i =0;for(constauto&element :std::as_const(list)) {if(i % frameCapacity ==0) {if(i) env->PopLocalFrame(nullptr);if(env->PushLocalFrame(frameCapacity) !=0)return ResultType{};} jobject object =QtJniTypes::Traits<ElementType>::convertToJni(env, element); env->SetObjectArrayElement(localArray, i, object);++i;}if(i) env->PopLocalFrame(nullptr);returnResultType(QJniObject::fromLocalRef(localArray));}namespace QtJniTypes {template<typename T>struct Traits<QJniArray<T>>{template<IfValidFieldType<T> =true>staticconstexpr autosignature(){returnCTString("[") + Traits<T>::signature();}staticautoconvertToJni(JNIEnv *,const QJniArray<T> &value){return value.arrayObject();}staticautoconvertFromJni(QJniObject &&object){return QJniArray<T>(std::move(object));}};template<typename T>struct Traits<QJniArrayMutableValueRef<T>> :public Traits<T> {};template<typename T>struct Traits<T,std::enable_if_t<QJniArrayBase::isCompatibleSourceContainer<T>>>{// QByteArray::value_type is char, which maps to 'C'; we need 'B', i.e. jbyteusing ElementType =std::conditional_t<std::is_same_v<T, QByteArray>, jbyte, typename T::value_type>;template<typename U = ElementType, IfValidFieldType<U> =true>staticconstexpr autosignature(){returnCTString("[") + Traits<ElementType>::signature();}staticautoconvertToJni(JNIEnv *env,const T &value){using QJniArrayType =decltype(QJniArrayBase::fromContainer(value));using ArrayType =decltype(std::declval<QJniArrayType>().arrayObject());return static_cast<ArrayType>(env->NewLocalRef(QJniArray(value).arrayObject()));}staticautoconvertFromJni(QJniObject &&object){// if we were to create a QJniArray from Type...using QJniArrayType =decltype(QJniArrayBase::fromContainer(std::declval<T>()));// then that QJniArray would have elements of typeusing ArrayType = typename QJniArrayType::Type;// construct a QJniArray from a jobject pointer of that typereturn QJniArray<ArrayType>(object.template object<jarray>()).toContainer();}};template<typename T>struct Traits<T,std::enable_if_t<std::is_array_v<T>>>{using ElementType =std::remove_extent_t<T>;template<typename U = ElementType, IfValidFieldType<U> =true>staticconstexpr autosignature(){static_assert(!std::is_array_v<ElementType>,"Traits::signature() does not handle multi-dimensional arrays");returnCTString("[") + Traits<U>::signature();}staticconstexpr autoconvertFromJni(QJniObject &&object){return QJniArray<ElementType>(std::move(object));}};} QT_END_NAMESPACE #endif#endif// QJNIARRAY_H
close