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