Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qjniarray.h
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QJNIARRAY_H
6#define QJNIARRAY_H
7
8#include <QtCore/qlist.h>
9
10#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
11#include <QtCore/qbytearray.h>
12#include <QtCore/qjniobject.h>
13
14#include <iterator>
15#include <QtCore/q26numeric.h>
16#include <QtCore/q20type_traits.h>
17#include <QtCore/q20utility.h>
18
19#if defined(Q_QDOC)
20using jsize = qint32;
21using jarray = jobject;
22#endif
23
24QT_BEGIN_NAMESPACE
25
26template <typename T> class QJniArray;
27template <typename T> struct QJniArrayMutableIterator;
28
29// forward declare here so that we don't have to include the private header
30namespace QtAndroidPrivate
31{
32 Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env);
33}
34
35template <typename T>
36struct QJniArrayIterator
37{
38private:
39 using VT = std::remove_const_t<T>;
40 friend class QJniArray<VT>;
41 friend struct QJniArrayMutableIterator<VT>;
42
43 // Since QJniArray doesn't hold values, we need a wrapper to be able to hand
44 // out a pointer to a value.
45 struct QJniArrayValueRef {
46 T ref;
47 const T *operator->() const { return &ref; }
48 };
49
50public:
51 QJniArrayIterator() = default;
52 constexpr QJniArrayIterator(const QJniArrayMutableIterator<VT> &other) noexcept
53 : m_index(other.m_index), m_array(other.m_array)
54 {}
55 constexpr QJniArrayIterator(QJniArrayMutableIterator<VT> &&other) noexcept
56 : m_index(std::exchange(other.m_index, -1)), m_array(std::exchange(other.m_array, nullptr))
57 {}
58
59 constexpr QJniArrayIterator(const QJniArrayIterator &other) noexcept = default;
60 constexpr QJniArrayIterator(QJniArrayIterator &&other) noexcept = default;
61 constexpr QJniArrayIterator &operator=(const QJniArrayIterator &other) noexcept = default;
62 constexpr QJniArrayIterator &operator=(QJniArrayIterator &&other) noexcept = default;
63
64 using difference_type = jsize;
65 using value_type = T;
66 using pointer = QJniArrayValueRef;
67 using reference = T; // difference to container requirements
68 using const_reference = reference;
69 using iterator_category = std::random_access_iterator_tag;
70
71 const_reference operator*() const
72 {
73 return m_array->at(m_index);
74 }
75
76 QJniArrayValueRef operator->() const
77 {
78 return {m_array->at(m_index)};
79 }
80
81 const_reference operator[](difference_type n) const
82 {
83 return m_array->at(m_index + n);
84 }
85
86 friend QJniArrayIterator &operator++(QJniArrayIterator &that) noexcept
87 {
88 ++that.m_index;
89 return that;
90 }
91 friend QJniArrayIterator operator++(QJniArrayIterator &that, int) noexcept
92 {
93 auto copy = that;
94 ++that;
95 return copy;
96 }
97 friend QJniArrayIterator operator+(const QJniArrayIterator &that, difference_type n) noexcept
98 {
99 return {that.m_index + n, that.m_array};
100 }
101 friend QJniArrayIterator operator+(difference_type n, const QJniArrayIterator &that) noexcept
102 {
103 return that + n;
104 }
105 friend QJniArrayIterator &operator+=(QJniArrayIterator &that, difference_type n) noexcept
106 {
107 that.m_index += n;
108 return that;
109 }
110 friend QJniArrayIterator &operator--(QJniArrayIterator &that) noexcept
111 {
112 --that.m_index;
113 return that;
114 }
115 friend QJniArrayIterator operator--(QJniArrayIterator &that, int) noexcept
116 {
117 auto copy = that;
118 --that;
119 return copy;
120 }
121 friend QJniArrayIterator operator-(const QJniArrayIterator &that, difference_type n) noexcept
122 {
123 return {that.m_index - n, that.m_array};
124 }
125 friend QJniArrayIterator operator-(difference_type n, const QJniArrayIterator &that) noexcept
126 {
127 return {n - that.m_index, that.m_array};
128 }
129 friend QJniArrayIterator &operator-=(QJniArrayIterator &that, difference_type n) noexcept
130 {
131 that.m_index -= n;
132 return that;
133 }
134 friend difference_type operator-(const QJniArrayIterator &lhs, const QJniArrayIterator &rhs)
135 {
136 Q_ASSERT(lhs.m_array == rhs.m_array);
137 return lhs.m_index - rhs.m_index;
138 }
139 void swap(QJniArrayIterator &other) noexcept
140 {
141 std::swap(m_index, other.m_index);
142 qt_ptr_swap(m_array, other.m_array);
143 }
144
145private:
146 friend constexpr bool comparesEqual(const QJniArrayIterator &lhs,
147 const QJniArrayIterator &rhs) noexcept
148 {
149 Q_ASSERT(lhs.m_array == rhs.m_array);
150 return lhs.m_index == rhs.m_index;
151 }
152 friend constexpr Qt::strong_ordering compareThreeWay(const QJniArrayIterator &lhs,
153 const QJniArrayIterator &rhs) noexcept
154 {
155 Q_ASSERT(lhs.m_array == rhs.m_array);
156 return Qt::compareThreeWay(lhs.m_index, rhs.m_index);
157 }
158 Q_DECLARE_STRONGLY_ORDERED(QJniArrayIterator)
159
160 qsizetype m_index = 0;
161 const QJniArray<VT> *m_array = nullptr;
162
163 QJniArrayIterator(qsizetype index, const QJniArray<VT> *array)
164 : m_index(index), m_array(array)
165 {}
166};
167
168template <typename T> // need to specialize traits for it, so can't be nested
169struct QJniArrayMutableValueRef;
170
171template <typename T>
172struct QJniArrayMutableIterator
173{
174private:
175 friend struct QJniArrayIterator<const T>;
176 friend struct QJniArrayMutableValueRef<T>;
177
178public:
179 constexpr QJniArrayMutableIterator() noexcept = default;
180 constexpr QJniArrayMutableIterator(const QJniArrayIterator<const T> &other) noexcept
181 : m_index(other.m_index), m_array(other.m_array)
182 {}
183 constexpr QJniArrayMutableIterator(QJniArrayIterator<const T> &&other) noexcept
184 : m_index(std::exchange(other.m_index, -1)), m_array(std::exchange(other.m_array, nullptr))
185 {}
186
187 constexpr QJniArrayMutableIterator(const QJniArrayMutableIterator &other) noexcept = default;
188 constexpr QJniArrayMutableIterator(QJniArrayMutableIterator &&other) noexcept = default;
189 constexpr QJniArrayMutableIterator &operator=(const QJniArrayMutableIterator &other) noexcept = default;
190 constexpr QJniArrayMutableIterator &operator=(QJniArrayMutableIterator &&other) noexcept = default;
191
192 using difference_type = jsize;
193 using value_type = T;
194 using pointer = QJniArrayMutableValueRef<T>;
195 using reference = QJniArrayMutableValueRef<T>; // difference to container requirements
196 using const_reference = T;
197 using iterator_category = std::random_access_iterator_tag;
198
199 const_reference operator*() const
200 {
201 return m_array->at(m_index);
202 }
203
204 reference operator*()
205 {
206 return {m_array->at(m_index), *this};
207 }
208
209 const pointer operator->() const
210 {
211 return {m_array->at(m_index)};
212 }
213
214 pointer operator->()
215 {
216 return {m_array->at(m_index), *this};
217 }
218
219 const_reference operator[](difference_type n) const
220 {
221 return m_array->at(m_index + n);
222 }
223 reference operator[](difference_type n)
224 {
225 return {m_array->at(m_index + n), *this};
226 }
227
228 friend QJniArrayMutableIterator &operator++(QJniArrayMutableIterator &that) noexcept
229 {
230 ++that.m_index;
231 return that;
232 }
233 friend QJniArrayMutableIterator operator++(QJniArrayMutableIterator &that, int) noexcept
234 {
235 auto copy = that;
236 ++that;
237 return copy;
238 }
239 friend QJniArrayMutableIterator operator+(const QJniArrayMutableIterator &that, difference_type n) noexcept
240 {
241 return {that.m_index + n, that.m_array};
242 }
243 friend QJniArrayMutableIterator operator+(difference_type n, const QJniArrayMutableIterator &that) noexcept
244 {
245 return that + n;
246 }
247 friend QJniArrayMutableIterator &operator+=(QJniArrayMutableIterator &that, difference_type n) noexcept
248 {
249 that.m_index += n;
250 return that;
251 }
252 friend QJniArrayMutableIterator &operator--(QJniArrayMutableIterator &that) noexcept
253 {
254 --that.m_index;
255 return that;
256 }
257 friend QJniArrayMutableIterator operator--(QJniArrayMutableIterator &that, int) noexcept
258 {
259 auto copy = that;
260 --that;
261 return copy;
262 }
263 friend QJniArrayMutableIterator operator-(const QJniArrayMutableIterator &that, difference_type n) noexcept
264 {
265 return {that.m_index - n, that.m_array};
266 }
267 friend QJniArrayMutableIterator operator-(difference_type n, const QJniArrayMutableIterator &that) noexcept
268 {
269 return {n - that.m_index, that.m_array};
270 }
271 friend QJniArrayMutableIterator &operator-=(QJniArrayMutableIterator &that, difference_type n) noexcept
272 {
273 that.m_index -= n;
274 return that;
275 }
276 friend difference_type operator-(const QJniArrayMutableIterator &lhs,
277 const QJniArrayMutableIterator &rhs)
278 {
279 Q_ASSERT(lhs.m_array == rhs.m_array);
280 return lhs.m_index - rhs.m_index;
281 }
282 void swap(QJniArrayMutableIterator &other) noexcept
283 {
284 std::swap(m_index, other.m_index);
285 qt_ptr_swap(m_array, other.m_array);
286 }
287
288private:
289 friend constexpr bool comparesEqual(const QJniArrayMutableIterator &lhs,
290 const QJniArrayMutableIterator &rhs)
291 {
292 Q_ASSERT(lhs.m_array == rhs.m_array);
293 return lhs.m_index == rhs.m_index;
294 }
295 friend constexpr bool comparesEqual(const QJniArrayMutableIterator &lhs,
296 const QJniArrayIterator<const T> &rhs)
297 {
298 Q_ASSERT(lhs.m_array == rhs.m_array);
299 return lhs.m_index == rhs.m_index;
300 }
301 friend constexpr Qt::strong_ordering compareThreeWay(const QJniArrayMutableIterator &lhs,
302 const QJniArrayMutableIterator &rhs)
303 {
304 Q_ASSERT(lhs.m_array == rhs.m_array);
305 return Qt::compareThreeWay(lhs.m_index, rhs.m_index);
306 }
307 friend constexpr Qt::strong_ordering compareThreeWay(const QJniArrayMutableIterator &lhs,
308 const QJniArrayIterator<const T> &rhs)
309 {
310 Q_ASSERT(lhs.m_array == rhs.m_array);
311 return Qt::compareThreeWay(lhs.m_index, rhs.m_index);
312 }
313 Q_DECLARE_STRONGLY_ORDERED_NON_NOEXCEPT(QJniArrayMutableIterator)
314 Q_DECLARE_STRONGLY_ORDERED_NON_NOEXCEPT(QJniArrayMutableIterator, QJniArrayIterator<const T>)
315
316 using VT = std::remove_const_t<T>;
317 friend class QJniArray<VT>;
318
319 qsizetype m_index = 0;
320 QJniArray<VT> *m_array = nullptr;
321
322 QJniArrayMutableIterator(qsizetype index, QJniArray<VT> *array)
323 : m_index(index), m_array(array)
324 {}
325};
326
327template <typename T> // need to specialize traits for it, so can't be nested
328struct QJniArrayMutableValueRef {
329 T value;
330 QJniArrayMutableIterator<T> back = {-1, nullptr};
331
332 operator T() const { return value; }
333 const T &operator*() const { return value; }
334 T &operator*() { return value; }
335
336 const T *operator->() const { return &value; }
337 T *operator->() = delete; // no write-back, so delete explicitly
338
339 QJniArrayMutableValueRef &operator=(const QJniArrayMutableValueRef &other)
340 {
341 return *this = *other;
342 }
343 QJniArrayMutableValueRef &operator=(QJniArrayMutableValueRef &&other)
344 {
345 return *this = std::move(*other);
346 }
347
348 QJniArrayMutableValueRef &operator=(const T &v)
349 {
350 Q_ASSERT(back.m_array);
351 value = v;
352 back.m_array->setValue(back.m_index, value);
353 return *this;
354 }
355 QJniArrayMutableValueRef &operator=(T &&v)
356 {
357 Q_ASSERT(back.m_array);
358 value = std::move(v);
359 back.m_array->setValue(back.m_index, value);
360 return *this;
361 }
362};
363
364class QJniArrayBase
365{
366 // for SFINAE'ing out the fromContainer named constructor
367 template <typename C, typename = void> struct IsSequentialContainerHelper : std::false_type
368 {
369 static constexpr bool isForwardIterable = false;
370 };
371 template <typename C>
372 struct IsSequentialContainerHelper<C, std::void_t<typename std::iterator_traits<typename C::const_iterator>::iterator_category,
373 typename C::value_type,
374 decltype(std::size(std::declval<C>()))
375 >
376 > : std::true_type
377 {
378 static constexpr bool isForwardIterable = std::is_convertible_v<
379 typename std::iterator_traits<typename C::const_iterator>::iterator_category,
380 std::forward_iterator_tag
381 >;
382 };
383 template <>
384 struct IsSequentialContainerHelper<QByteArray, void>
385 {
386 static constexpr bool isForwardIterable = true;
387 };
388
389 template <typename C, typename = void> struct IsContiguousContainerHelper : std::false_type {};
390 template <typename C>
391 struct IsContiguousContainerHelper<C, std::void_t<decltype(std::data(std::declval<C>())),
392 decltype(std::size(std::declval<C>())),
393 typename C::value_type
394 >
395 > : std::true_type {};
396
397 template <typename C, typename = void> struct HasEmplaceBackTest : std::false_type {};
398 template <typename C> struct HasEmplaceBackTest<C,
399 std::void_t<decltype(std::declval<C>().emplace_back(std::declval<typename C::value_type>()))>
400 > : std::true_type
401 {};
402
403
404protected:
405 // these are used in QJniArray
406 template <typename C, typename = void>
407 struct ElementTypeHelper
408 {
409 static constexpr bool isObject = false;
410 static constexpr bool isPrimitive = false;
411 };
412 template <typename C>
413 struct ElementTypeHelper<C, std::void_t<typename C::value_type>>
414 {
415 using E = typename C::value_type;
416 static constexpr bool isObject = QtJniTypes::isObjectType<E>();
417 static constexpr bool isPrimitive = QtJniTypes::isPrimitiveType<E>();
418 };
419
420 template <typename CRef, typename C = q20::remove_cvref_t<CRef>>
421 static constexpr bool isContiguousContainer = IsContiguousContainerHelper<C>::value;
422
423 template <typename From, typename To>
424 using if_convertible = std::enable_if_t<QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase<From, To>::value, bool>;
425 template <typename From, typename To>
426 using unless_convertible = std::enable_if_t<!QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase<From, To>::value, bool>;
427
428 // helpers for toContainer
429 template <typename E> struct ToContainerHelper { using type = QList<E>; };
430 template <> struct ToContainerHelper<jstring> { using type = QStringList; };
431 template <> struct ToContainerHelper<jbyte> { using type = QByteArray; };
432 template <> struct ToContainerHelper<char> { using type = QByteArray; };
433
434 template <typename E>
435 using ToContainerType = typename ToContainerHelper<E>::type;
436
437 template <typename E, typename CRef, typename C = q20::remove_cvref_t<CRef>>
438 static constexpr bool isCompatibleTargetContainer =
439 (QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase<E, typename C::value_type>::value
440 || QtPrivate::AreArgumentsConvertibleWithoutNarrowingBase<typename ToContainerType<E>::value_type,
441 typename C::value_type>::value
442 || (std::is_base_of_v<QtJniTypes::JObjectBase, E> && std::is_same_v<typename C::value_type, QString>))
443 && (qxp::is_detected_v<HasEmplaceBackTest, C>
444 || (isContiguousContainer<C> && ElementTypeHelper<C>::isPrimitive));
445
446public:
447 using size_type = jsize;
448 using difference_type = size_type;
449
450 operator QJniObject() const { return m_object; }
451
452 template <typename T = jobject>
453 T object() const { return m_object.object<T>(); }
454 bool isValid() const { return m_object.isValid(); }
455 bool isEmpty() const { return size() == 0; }
456
457 size_type size() const
458 {
459 if (jarray array = m_object.object<jarray>())
460 return jniEnv()->GetArrayLength(array);
461 return 0;
462 }
463
464 // We can create an array from any forward-iterable container, and optimize
465 // for contiguous containers holding primitive elements. QJniArray is a
466 // forward-iterable container, so explicitly remove that from the overload
467 // set so that the copy constructors get used instead.
468 // Used also in the deduction guide, so must be public
469 template <typename C>
470 using IsSequentialOrContiguous = std::bool_constant<
471 IsSequentialContainerHelper<C>::isForwardIterable
472 || (isContiguousContainer<C> && ElementTypeHelper<C>::isPrimitive)
473 >;
474 template <typename CRef, typename C = q20::remove_cvref_t<CRef>>
475 static constexpr bool isCompatibleSourceContainer = std::conjunction_v<
476 std::negation<std::is_same<QString, C>>,
477 IsSequentialOrContiguous<C>,
478 std::negation<std::is_base_of<QJniArrayBase, C>>
479 >;
480
481 template <typename C>
482 using if_compatible_source_container = std::enable_if_t<isCompatibleSourceContainer<C>, bool>;
483 template <typename T, typename C>
484 using if_compatible_target_container = std::enable_if_t<isCompatibleTargetContainer<T, C>, bool>;
485
486 template <typename Container, if_compatible_source_container<Container> = true>
487 static auto fromContainer(Container &&container)
488 {
489 verifySize(std::size(container));
490 using ElementType = typename std::remove_reference_t<Container>::value_type;
491 if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>,
492 std::remove_pointer_t<ElementType>>) {
493 return makeObjectArray(std::forward<Container>(container));
494 } else if constexpr (std::disjunction_v<std::is_same<ElementType, QJniObject>,
495 std::is_same<ElementType, QString>,
496 std::is_base_of<QtJniTypes::JObjectBase, ElementType>
497 >) {
498 return QJniArray<ElementType>(makeObjectArray(std::forward<Container>(container)).arrayObject());
499 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jfloat>) {
500 return makeArray<jfloat>(std::forward<Container>(container), &JNIEnv::NewFloatArray,
501 &JNIEnv::SetFloatArrayRegion);
502 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jdouble>) {
503 return makeArray<jdouble>(std::forward<Container>(container), &JNIEnv::NewDoubleArray,
504 &JNIEnv::SetDoubleArrayRegion);
505 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jboolean>) {
506 return makeArray<jboolean>(std::forward<Container>(container), &JNIEnv::NewBooleanArray,
507 &JNIEnv::SetBooleanArrayRegion);
508 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jbyte>
509 || std::is_same_v<ElementType, char>) {
510 return makeArray<jbyte>(std::forward<Container>(container), &JNIEnv::NewByteArray,
511 &JNIEnv::SetByteArrayRegion);
512 } else if constexpr (std::disjunction_v<std::is_same<ElementType, jchar>,
513 std::is_same<ElementType, QChar>>) {
514 return makeArray<jchar>(std::forward<Container>(container), &JNIEnv::NewCharArray,
515 &JNIEnv::SetCharArrayRegion);
516 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jshort>) {
517 return makeArray<jshort>(std::forward<Container>(container), &JNIEnv::NewShortArray,
518 &JNIEnv::SetShortArrayRegion);
519 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jint>) {
520 return makeArray<jint>(std::forward<Container>(container), &JNIEnv::NewIntArray,
521 &JNIEnv::SetIntArrayRegion);
522 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jlong>) {
523 return makeArray<jlong>(std::forward<Container>(container), &JNIEnv::NewLongArray,
524 &JNIEnv::SetLongArrayRegion);
525 } else {
526 static_assert(QtPrivate::type_dependent_false<ElementType>(),
527 "Don't know how to make QJniArray for this element type");
528 }
529 }
530
531protected:
532 QJniArrayBase() = default;
533 ~QJniArrayBase() = default;
534
535 explicit QJniArrayBase(const QJniArrayBase &other) = default;
536 explicit QJniArrayBase(QJniArrayBase &&other) noexcept = default;
537 QJniArrayBase &operator=(const QJniArrayBase &other) = default;
538 QJniArrayBase &operator=(QJniArrayBase &&other) noexcept = default;
539
540 explicit QJniArrayBase(jarray array)
541 : m_object(static_cast<jobject>(array))
542 {
543 }
544 explicit QJniArrayBase(const QJniObject &object)
545 : m_object(object)
546 {}
547 explicit QJniArrayBase(QJniObject &&object) noexcept
548 : m_object(std::move(object))
549 {}
550 QJniArrayBase &operator=(const QJniObject &object)
551 {
552 m_object = object;
553 return *this;
554 }
555 QJniArrayBase &operator=(QJniObject &&object) noexcept
556 {
557 m_object = std::move(object);
558 return *this;
559 }
560
561 JNIEnv *jniEnv() const noexcept { return QJniEnvironment::getJniEnv(); }
562
563 template <typename ElementType, typename List, typename NewFn, typename SetFn>
564 static auto makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion);
565 template <typename List>
566 static auto makeObjectArray(List &&list);
567 template <typename ElementType>
568 static auto makeEmptyArray(size_type size)
569 {
570 auto env = QJniEnvironment();
571 if constexpr (std::disjunction_v<std::is_base_of<std::remove_pointer_t<jobject>,
572 std::remove_pointer_t<ElementType>>,
573 std::is_same<ElementType, QJniObject>,
574 std::is_same<ElementType, QString>,
575 std::is_base_of<QtJniTypes::JObjectBase, ElementType>
576 >) {
577 using ResultType = decltype(QtJniTypes::Traits<ElementType>::convertToJni(nullptr, {}));
578 const auto className = QtJniTypes::Traits<ResultType>::className();
579 jclass elementClass = env.findClass(className);
580 if (!elementClass) {
581 env.checkAndClearExceptions();
582 return jobjectArray(nullptr);
583 }
584 return env->NewObjectArray(size, elementClass, nullptr);
585 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jfloat>) {
586 return env->NewFloatArray(size);
587 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jdouble>) {
588 return env->NewDoubleArray(size);
589 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jboolean>) {
590 return env->NewBooleanArray(size);
591 } else if constexpr (std::disjunction_v<std::is_same<ElementType, jbyte>,
592 std::is_same<ElementType, char>>) {
593 return env->NewByteArray(size);
594 } else if constexpr (std::disjunction_v<std::is_same<ElementType, jchar>,
595 std::is_same<ElementType, QChar>>) {
596 return env->NewCharArray(size);
597 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jshort>) {
598 return env->NewShortArray(size);
599 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jint>) {
600 return env->NewIntArray(size);
601 } else if constexpr (QtJniTypes::sameTypeForJni<ElementType, jlong>) {
602 return env->NewLongArray(size);
603 } else {
604 static_assert(QtPrivate::type_dependent_false<ElementType>(),
605 "Don't know how to make QJniArray for this element type");
606 }
607 }
608
609 void swap(QJniArrayBase &other) noexcept { m_object.swap(other.m_object); }
610
611private:
612 template <typename container_size_type>
613 static void verifySize(container_size_type size)
614 {
615 if (!q20::in_range<size_type>(size))
616 qWarning("QJniArray::fromContainer: Container is too large for Java and will be truncated!");
617 }
618
619 QJniObject m_object;
620};
621
622template <typename T>
623class QJniArray : public QJniArrayBase
624{
625 friend struct QJniArrayIterator<T>;
626
627 template <typename C>
628 using CanReserveTest = decltype(std::declval<C>().reserve(0));
629 template <typename C>
630 static constexpr bool canReserve = qxp::is_detected_v<CanReserveTest, C>;
631
632public:
633 using Type = T;
634
635 using value_type = T;
636 using iterator = QJniArrayMutableIterator<T>;
637 using reverse_iterator = std::reverse_iterator<iterator>;
638 using const_iterator = QJniArrayIterator<const T>;
639 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
640
641 using reference = typename iterator::reference;
642 using const_reference = typename const_iterator::const_reference;
643
644 QJniArray() = default;
645 explicit QJniArray(jarray array) : QJniArrayBase(array) {}
646 explicit QJniArray(const QJniObject &object) : QJniArrayBase(object) {}
647 explicit QJniArray(QJniObject &&object) noexcept : QJniArrayBase(std::move(object)) {}
648
649 template <typename Other, if_convertible<Other, T> = true>
650 QJniArray(const QJniArray<Other> &other)
651 : QJniArrayBase(other)
652 {
653 }
654 template <typename Other, if_convertible<Other, T> = true>
655 QJniArray(QJniArray<Other> &&other) noexcept
656 : QJniArrayBase(std::move(other))
657 {
658 }
659 template <typename Other, if_convertible<Other, T> = true>
660 QJniArray &operator=(const QJniArray<Other> &other)
661 {
662 QJniArrayBase::operator=(QJniObject(other));
663 return *this;
664 }
665 template <typename Other, if_convertible<Other, T> = true>
666 QJniArray &operator=(QJniArray<Other> &&other) noexcept
667 {
668 QJniArray moved(std::move(other));
669 swap(moved);
670 return *this;
671 }
672 // explicitly delete to disable detour via operator QJniObject()
673 template <typename Other, unless_convertible<Other, T> = true>
674 QJniArray(const QJniArray<Other> &other) = delete;
675 template <typename Other, unless_convertible<Other, T> = true>
676 QJniArray(QJniArray<Other> &&other) noexcept = delete;
677
678 template <typename Container, if_compatible_source_container<Container> = true>
679 explicit QJniArray(Container &&container)
680 : QJniArrayBase(QJniArrayBase::fromContainer(std::forward<Container>(container)))
681 {
682 }
683
684 Q_IMPLICIT inline QJniArray(std::initializer_list<T> list)
685 : QJniArrayBase(QJniArrayBase::fromContainer(list))
686 {
687 }
688
689 explicit QJniArray(size_type size)
690 : QJniArrayBase(makeEmptyArray<T>(size))
691 {}
692
693 ~QJniArray() = default;
694
695 auto arrayObject() const
696 {
697 if constexpr (QtJniTypes::isObjectType<T>())
698 return object<jobjectArray>();
699 else if constexpr (QtJniTypes::sameTypeForJni<T, jbyte>)
700 return object<jbyteArray>();
701 else if constexpr (QtJniTypes::sameTypeForJni<T, jchar>)
702 return object<jcharArray>();
703 else if constexpr (QtJniTypes::sameTypeForJni<T, jboolean>)
704 return object<jbooleanArray>();
705 else if constexpr (QtJniTypes::sameTypeForJni<T, jshort>)
706 return object<jshortArray>();
707 else if constexpr (QtJniTypes::sameTypeForJni<T, jint>)
708 return object<jintArray>();
709 else if constexpr (QtJniTypes::sameTypeForJni<T, jlong>)
710 return object<jlongArray>();
711 else if constexpr (QtJniTypes::sameTypeForJni<T, jfloat>)
712 return object<jfloatArray>();
713 else if constexpr (QtJniTypes::sameTypeForJni<T, jdouble>)
714 return object<jdoubleArray>();
715 else
716 return object<jarray>();
717 }
718
719 const_iterator begin() const noexcept { return {0, this}; }
720 const_iterator constBegin() const noexcept { return begin(); }
721 const_iterator cbegin() const noexcept { return begin(); }
722 const_iterator end() const noexcept { return {size(), this}; }
723 const_iterator constEnd() const noexcept { return {end()}; }
724 const_iterator cend() const noexcept { return {end()}; }
725
726 iterator begin() noexcept { return {0, this}; }
727 iterator end() noexcept { return {size(), this}; }
728
729 const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
730 const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
731 const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); }
732 const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }
733
734 reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
735 reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
736
737 const_reference operator[](size_type i) const { return at(i); }
738 reference operator[](size_type i) { return reference{at(i), iterator{i, this}}; }
739 const_reference at(size_type i) const
740 {
741 JNIEnv *env = jniEnv();
742 if constexpr (QtJniTypes::isObjectType<T>()) {
743 jobject element = env->GetObjectArrayElement(object<jobjectArray>(), i);
744 if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>)
745 return static_cast<T>(element);
746 else
747 return QtJniTypes::Traits<T>::convertFromJni(QJniObject::fromLocalRef(element));
748 } else {
749 T res = {};
750 if constexpr (QtJniTypes::sameTypeForJni<T, jbyte>)
751 env->GetByteArrayRegion(object<jbyteArray>(), i, 1, &res);
752 else if constexpr (QtJniTypes::sameTypeForJni<T, jchar>)
753 env->GetCharArrayRegion(object<jcharArray>(), i, 1, &res);
754 else if constexpr (QtJniTypes::sameTypeForJni<T, jboolean>)
755 env->GetBooleanArrayRegion(object<jbooleanArray>(), i, 1, &res);
756 else if constexpr (QtJniTypes::sameTypeForJni<T, jshort>)
757 env->GetShortArrayRegion(object<jshortArray>(), i, 1, &res);
758 else if constexpr (QtJniTypes::sameTypeForJni<T, jint>)
759 env->GetIntArrayRegion(object<jintArray>(), i, 1, &res);
760 else if constexpr (QtJniTypes::sameTypeForJni<T, jlong>)
761 env->GetLongArrayRegion(object<jlongArray>(), i, 1, &res);
762 else if constexpr (QtJniTypes::sameTypeForJni<T, jfloat>)
763 env->GetFloatArrayRegion(object<jfloatArray>(), i, 1, &res);
764 else if constexpr (QtJniTypes::sameTypeForJni<T, jdouble>)
765 env->GetDoubleArrayRegion(object<jdoubleArray>(), i, 1, &res);
766 return res;
767 }
768 }
769
770 void setValue(size_type i, const_reference &val)
771 {
772 JNIEnv *env = jniEnv();
773
774 if constexpr (QtJniTypes::isObjectType<T>()) {
775 QtJniTypes::Detail::LocalFrame<T> frame(env);
776 jobject element = frame.convertToJni(val);
777 env->SetObjectArrayElement(object<jobjectArray>(), i, element);
778 } else { // primitive types
779 if constexpr (QtJniTypes::sameTypeForJni<T, jbyte>)
780 env->SetByteArrayRegion(object<jbyteArray>(), i, 1, &val);
781 else if constexpr (QtJniTypes::sameTypeForJni<T, jchar>)
782 env->SetCharArrayRegion(object<jcharArray>(), i, 1, &val);
783 else if constexpr (QtJniTypes::sameTypeForJni<T, jboolean>)
784 env->SetBooleanArrayRegion(object<jbooleanArray>(), i, 1, &val);
785 else if constexpr (QtJniTypes::sameTypeForJni<T, jshort>)
786 env->SetShortArrayRegion(object<jshortArray>(), i, 1, &val);
787 else if constexpr (QtJniTypes::sameTypeForJni<T, jint>)
788 env->SetIntArrayRegion(object<jintArray>(), i, 1, &val);
789 else if constexpr (QtJniTypes::sameTypeForJni<T, jlong>)
790 env->SetLongArrayRegion(object<jlongArray>(), i, 1, &val);
791 else if constexpr (QtJniTypes::sameTypeForJni<T, jfloat>)
792 env->SetFloatArrayRegion(object<jfloatArray>(), i, 1, &val);
793 else if constexpr (QtJniTypes::sameTypeForJni<T, jdouble>)
794 env->SetDoubleArrayRegion(object<jdoubleArray>(), i, 1, &val);
795 }
796 }
797
798 template <typename Container = ToContainerType<T>, if_compatible_target_container<T, Container> = true>
799 Container toContainer(Container &&container = {}) const
800 {
801 const qsizetype sz = size();
802 if (!sz)
803 return std::forward<Container>(container);
804 JNIEnv *env = jniEnv();
805
806 using ContainerType = q20::remove_cvref_t<Container>;
807
808 if constexpr (canReserve<ContainerType>)
809 container.reserve(sz);
810 if constexpr (std::is_same_v<typename ContainerType::value_type, QString>) {
811 for (auto element : *this) {
812 if constexpr (std::is_same_v<decltype(element), QString>) {
813 container.emplace_back(element);
814 } else if constexpr (std::is_same_v<decltype(element), jstring>) {
815 container.emplace_back(element ? QtJniTypes::Detail::toQString(element, env)
816 : QString{});
817 } else {
818 container.emplace_back(QJniObject(element).toString());
819 }
820 }
821 } else if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>) {
822 for (auto element : *this)
823 container.emplace_back(element);
824 } else if constexpr (isContiguousContainer<ContainerType>) {
825 container.resize(sz);
826 if constexpr (QtJniTypes::sameTypeForJni<T, jbyte>) {
827 env->GetByteArrayRegion(object<jbyteArray>(),
828 0, sz,
829 reinterpret_cast<jbyte *>(container.data()));
830 } else if constexpr (QtJniTypes::sameTypeForJni<T, jchar>) {
831 env->GetCharArrayRegion(object<jcharArray>(),
832 0, sz, container.data());
833 } else if constexpr (QtJniTypes::sameTypeForJni<T, jboolean>) {
834 env->GetBooleanArrayRegion(object<jbooleanArray>(),
835 0, sz, container.data());
836 } else if constexpr (QtJniTypes::sameTypeForJni<T, jshort>) {
837 env->GetShortArrayRegion(object<jshortArray>(),
838 0, sz, container.data());
839 } else if constexpr (QtJniTypes::sameTypeForJni<T, jint>) {
840 env->GetIntArrayRegion(object<jintArray>(),
841 0, sz, container.data());
842 } else if constexpr (QtJniTypes::sameTypeForJni<T, jlong>) {
843 env->GetLongArrayRegion(object<jlongArray>(),
844 0, sz, container.data());
845 } else if constexpr (QtJniTypes::sameTypeForJni<T, jfloat>) {
846 env->GetFloatArrayRegion(object<jfloatArray>(),
847 0, sz, container.data());
848 } else if constexpr (QtJniTypes::sameTypeForJni<T, jdouble>) {
849 env->GetDoubleArrayRegion(object<jdoubleArray>(),
850 0, sz, container.data());
851 } else {
852 static_assert(QtPrivate::type_dependent_false<T>(),
853 "Don't know how to copy data from a QJniArray of this type");
854 }
855 } else {
856 for (auto e : *this)
857 container.emplace_back(e);
858 }
859 return std::forward<Container>(container);
860 }
861};
862
863// Deduction guide so that we can construct as 'QJniArray list(Container<T>)'. Since
864// fromContainer() maps several C++ types to the same JNI type (e.g. both jboolean and
865// bool become QJniArray<jboolean>), we have to deduce to what fromContainer() would
866// give us.
867template <typename Container, QJniArrayBase::if_compatible_source_container<Container> = true>
868QJniArray(Container) -> QJniArray<typename decltype(QJniArrayBase::fromContainer(std::declval<Container>()))::value_type>;
869
870template <typename ElementType, typename List, typename NewFn, typename SetFn>
871auto QJniArrayBase::makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion)
872{
873 const size_type length = size_type(std::size(list));
874 JNIEnv *env = QJniEnvironment::getJniEnv();
875 auto localArray = (env->*newArray)(length);
876 if (env->ExceptionCheck()) {
877 if (localArray)
878 env->DeleteLocalRef(localArray);
879 return QJniArray<ElementType>();
880 }
881
882 if (length) {
883 // can't use static_cast here because we have signed/unsigned mismatches
884 if constexpr (isContiguousContainer<List>) {
885 (env->*setRegion)(localArray, 0, length,
886 reinterpret_cast<const ElementType *>(std::data(std::as_const(list))));
887 } else {
888 size_type i = 0;
889 for (const auto &e : std::as_const(list))
890 (env->*setRegion)(localArray, i++, 1, reinterpret_cast<const ElementType *>(&e));
891 }
892 }
893 return QJniArray<ElementType>(QJniObject::fromLocalRef(localArray));
894};
895
896template <typename List>
897auto QJniArrayBase::makeObjectArray(List &&list)
898{
899 using ElementType = typename q20::remove_cvref_t<List>::value_type;
900 using ResultType = QJniArray<decltype(QtJniTypes::Traits<ElementType>::convertToJni(nullptr,
901 {}))>;
902
903 if (std::size(list) == 0)
904 return ResultType();
905
906 JNIEnv *env = QJniEnvironment::getJniEnv();
907 const size_type length = q26::saturate_cast<size_type>(std::size(list));
908
909 // this assumes that all objects in the list have the same class
910 jclass elementClass = nullptr;
911 if constexpr (std::disjunction_v<std::is_same<ElementType, QJniObject>,
912 std::is_base_of<QtJniTypes::JObjectBase, ElementType>>) {
913 elementClass = std::begin(list)->objectClass();
914 } else if constexpr (std::is_same_v<ElementType, QString>) {
915 elementClass = QtAndroidPrivate::findClass("java/lang/String", env);
916 } else {
917 elementClass = env->GetObjectClass(*std::begin(list));
918 }
919 auto localArray = env->NewObjectArray(length, elementClass, nullptr);
920 if (env->ExceptionCheck()) {
921 if (localArray)
922 env->DeleteLocalRef(localArray);
923 return ResultType();
924 }
925
926 // explicitly manage the frame for local references in chunks of 100
927 constexpr jint frameCapacity = 100;
928 qsizetype i = 0;
929 for (const auto &element : std::as_const(list)) {
930 if (i % frameCapacity == 0) {
931 if (i)
932 env->PopLocalFrame(nullptr);
933 if (env->PushLocalFrame(frameCapacity) != 0)
934 return ResultType{};
935 }
936 jobject object = QtJniTypes::Traits<ElementType>::convertToJni(env, element);
937 env->SetObjectArrayElement(localArray, i, object);
938 ++i;
939 }
940 if (i)
941 env->PopLocalFrame(nullptr);
942 return ResultType(QJniObject::fromLocalRef(localArray));
943}
944
945namespace QtJniTypes
946{
947template <typename T> struct Traits<QJniArray<T>>
948{
949 template <IfValidFieldType<T> = true>
950 static constexpr auto signature()
951 {
952 return CTString("[") + Traits<T>::signature();
953 }
954 static auto convertToJni(JNIEnv *, const QJniArray<T> &value)
955 {
956 return value.arrayObject();
957 }
958 static auto convertFromJni(QJniObject &&object)
959 {
960 return QJniArray<T>(std::move(object));
961 }
962};
963
964template <typename T> struct Traits<QJniArrayMutableValueRef<T>> : public Traits<T> {};
965
966template<typename T> struct Traits<T, std::enable_if_t<QJniArrayBase::isCompatibleSourceContainer<T>>>
967{
968 // QByteArray::value_type is char, which maps to 'C'; we need 'B', i.e. jbyte
969 using ElementType = std::conditional_t<std::is_same_v<T, QByteArray>,
970 jbyte, typename T::value_type>;
971
972 template <typename U = ElementType, IfValidFieldType<U> = true>
973 static constexpr auto signature()
974 {
975 return CTString("[") + Traits<ElementType>::signature();
976 }
977
978 static auto convertToJni(JNIEnv *env, const T &value)
979 {
980 using QJniArrayType = decltype(QJniArrayBase::fromContainer(value));
981 using ArrayType = decltype(std::declval<QJniArrayType>().arrayObject());
982 return static_cast<ArrayType>(env->NewLocalRef(QJniArray(value).arrayObject()));
983 }
984
985 static auto convertFromJni(QJniObject &&object)
986 {
987 // if we were to create a QJniArray from Type...
988 using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::declval<T>()));
989 // then that QJniArray would have elements of type
990 using ArrayType = typename QJniArrayType::Type;
991 // construct a QJniArray from a jobject pointer of that type
992 return QJniArray<ArrayType>(object.template object<jarray>()).toContainer();
993 }
994};
995
996template<typename T> struct Traits<T, std::enable_if_t<std::is_array_v<T>>>
997{
998 using ElementType = std::remove_extent_t<T>;
999
1000 template <typename U = ElementType, IfValidFieldType<U> = true>
1001 static constexpr auto signature()
1002 {
1003 static_assert(!std::is_array_v<ElementType>,
1004 "Traits::signature() does not handle multi-dimensional arrays");
1005 return CTString("[") + Traits<U>::signature();
1006 }
1007
1008 static constexpr auto convertFromJni(QJniObject &&object)
1009 {
1010 return QJniArray<ElementType>(std::move(object));
1011 }
1012};
1013}
1014
1015QT_END_NAMESPACE
1016
1017#endif
1018
1019#endif // QJNIARRAY_H