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
qjnitypes_impl.h
Go to the documentation of this file.
1// Copyright (C) 2022 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 QJNITYPES_IMPL_H
6#define QJNITYPES_IMPL_H
7
8#include <QtCore/qstring.h>
9
10#include <QtCore/q26numeric.h>
11#include <QtCore/q20type_traits.h>
12#include <QtCore/q20utility.h>
13
14#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
15#include <jni.h>
16
17QT_BEGIN_NAMESPACE
18
19class QJniObject;
20
21namespace QtJniTypes
22{
23
24namespace Detail
25{
26static inline jstring fromQString(QStringView string, JNIEnv *env)
27{
28 if (!q20::in_range<jsize>(string.size()))
29 qWarning("String is too large for a Java string and will be truncated");
30 const jsize length = q26::saturating_cast<jsize>(string.size());
31 return env->NewString(reinterpret_cast<const jchar*>(string.constData()), length);
32}
33
34static inline QString toQString(jstring string, JNIEnv *env)
35{
36 if (!string)
37 return QString();
38 const jsize length = env->GetStringLength(string);
39 QString res(length, Qt::Uninitialized);
40 env->GetStringRegion(string, 0, length, reinterpret_cast<jchar *>(res.data_ptr().data()));
41 return res;
42}
43} // namespace Detail
44
45// a constexpr type for string literals of any character width, aware of the length
46// of the string.
47template<size_t N_WITH_NULL, typename BaseType = char>
48struct CTString
49{
50 BaseType m_data[N_WITH_NULL] = {};
51
52 constexpr CTString() noexcept {}
53 // Can be instantiated (only) with a string literal
54 constexpr explicit CTString(const BaseType (&data)[N_WITH_NULL]) noexcept
55 {
56 for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
57 m_data[i] = data[i];
58 }
59
60 constexpr BaseType at(size_t i) const { return m_data[i]; }
61 constexpr BaseType operator[](size_t i) const { return at(i); }
62 static constexpr size_t size() noexcept { return N_WITH_NULL; }
63 constexpr operator const BaseType *() const noexcept { return m_data; }
64 constexpr const BaseType *data() const noexcept { return m_data; }
65 template<size_t N2_WITH_NULL>
66 constexpr bool startsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
67 {
68 if constexpr (N2_WITH_NULL > N_WITH_NULL) {
69 return false;
70 } else {
71 for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) {
72 if (m_data[i] != lit[i])
73 return false;
74 }
75 }
76 return true;
77 }
78 constexpr bool startsWith(BaseType c) const noexcept
79 {
80 return N_WITH_NULL > 1 && m_data[0] == c;
81 }
82 template<size_t N2_WITH_NULL>
83 constexpr bool endsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
84 {
85 if constexpr (N2_WITH_NULL > N_WITH_NULL) {
86 return false;
87 } else {
88 for (size_t i = 0; i < N2_WITH_NULL; ++i) {
89 if (m_data[N_WITH_NULL - i - 1] != lit[N2_WITH_NULL - i - 1])
90 return false;
91 }
92 }
93 return true;
94 }
95 constexpr bool endsWith(BaseType c) const noexcept
96 {
97 return N_WITH_NULL > 1 && m_data[N_WITH_NULL - 2] == c;
98 }
99
100 template<size_t N2_WITH_NULL>
101 friend inline constexpr bool operator==(const CTString<N_WITH_NULL> &lhs,
102 const CTString<N2_WITH_NULL> &rhs) noexcept
103 {
104 if constexpr (N_WITH_NULL != N2_WITH_NULL) {
105 return false;
106 } else {
107 for (size_t i = 0; i < N_WITH_NULL - 1; ++i) {
108 if (lhs.at(i) != rhs.at(i))
109 return false;
110 }
111 }
112 return true;
113 }
114
115 template<size_t N2_WITH_NULL>
116 friend inline constexpr bool operator!=(const CTString<N_WITH_NULL> &lhs,
117 const CTString<N2_WITH_NULL> &rhs) noexcept
118 {
119 return !operator==(lhs, rhs);
120 }
121
122 template<size_t N2_WITH_NULL>
123 friend inline constexpr bool operator==(const CTString<N_WITH_NULL> &lhs,
124 const BaseType (&rhs)[N2_WITH_NULL]) noexcept
125 {
126 return operator==(lhs, CTString<N2_WITH_NULL>(rhs));
127 }
128 template<size_t N2_WITH_NULL>
129 friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL],
130 const CTString<N_WITH_NULL> &rhs) noexcept
131 {
132 return operator==(CTString<N2_WITH_NULL>(lhs), rhs);
133 }
134
135 template<size_t N2_WITH_NULL>
136 friend inline constexpr bool operator!=(const CTString<N_WITH_NULL> &lhs,
137 const BaseType (&rhs)[N2_WITH_NULL]) noexcept
138 {
139 return operator!=(lhs, CTString<N2_WITH_NULL>(rhs));
140 }
141 template<size_t N2_WITH_NULL>
142 friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL],
143 const CTString<N_WITH_NULL> &rhs) noexcept
144 {
145 return operator!=(CTString<N2_WITH_NULL>(lhs), rhs);
146 }
147
148 template<size_t N2_WITH_NULL>
149 friend inline constexpr auto operator+(const CTString<N_WITH_NULL> &lhs,
150 const CTString<N2_WITH_NULL> &rhs) noexcept
151 {
152 char data[N_WITH_NULL + N2_WITH_NULL - 1] = {};
153 for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
154 data[i] = lhs[i];
155 for (size_t i = 0; i < N2_WITH_NULL - 1; ++i)
156 data[N_WITH_NULL - 1 + i] = rhs[i];
157 return CTString<N_WITH_NULL + N2_WITH_NULL - 1>(data);
158 }
159};
160
161// Helper types that allow us to disable variadic overloads that would conflict
162// with overloads that take a const char*.
163template<typename T, size_t N = 0> struct IsStringType : std::false_type {};
164template<> struct IsStringType<const char *, 0> : std::true_type {};
165template<> struct IsStringType<const char *&, 0> : std::true_type {};
166template<size_t N> struct IsStringType<CTString<N>> : std::true_type {};
167template<size_t N> struct IsStringType<const char[N]> : std::true_type {};
168template<size_t N> struct IsStringType<const char(&)[N]> : std::true_type {};
169template<size_t N> struct IsStringType<char[N]> : std::true_type {};
170
171template <typename T, typename = void>
172struct Traits {
173 // The return type of className/signature becomes void for any type
174 // not handled here. This indicates that the Traits type is not specialized
175 // for the respective type, which we use to detect invalid types in the
176 // IfValidSignatureTypes and IfValidFieldType predicates below.
177
178 static constexpr auto className()
179 {
180 if constexpr (std::is_same_v<T, jstring>)
181 return CTString("java/lang/String");
182 else if constexpr (std::is_same_v<T, jobject>)
183 return CTString("java/lang/Object");
184 else if constexpr (std::is_same_v<T, jclass>)
185 return CTString("java/lang/Class");
186 else if constexpr (std::is_same_v<T, jthrowable>)
187 return CTString("java/lang/Throwable");
188 // else: return void -> not implemented
189 }
190
191 static constexpr auto signature()
192 {
193 if constexpr (!std::is_same_v<decltype(className()), void>) {
194 // the type signature of any object class is L<className>;
195 return CTString("L") + className() + CTString(";");
196 } else if constexpr (std::is_same_v<T, jobjectArray>) {
197 return CTString("[Ljava/lang/Object;");
198 } else if constexpr (std::is_same_v<T, jbooleanArray>) {
199 return CTString("[Z");
200 } else if constexpr (std::is_same_v<T, jbyteArray>) {
201 return CTString("[B");
202 } else if constexpr (std::is_same_v<T, jshortArray>) {
203 return CTString("[S");
204 } else if constexpr (std::is_same_v<T, jintArray>) {
205 return CTString("[I");
206 } else if constexpr (std::is_same_v<T, jlongArray>) {
207 return CTString("[J");
208 } else if constexpr (std::is_same_v<T, jfloatArray>) {
209 return CTString("[F");
210 } else if constexpr (std::is_same_v<T, jdoubleArray>) {
211 return CTString("[D");
212 } else if constexpr (std::is_same_v<T, jcharArray>) {
213 return CTString("[C");
214 } else if constexpr (std::is_same_v<T, jboolean>) {
215 return CTString("Z");
216 } else if constexpr (std::is_same_v<T, bool>) {
217 return CTString("Z");
218 } else if constexpr (std::is_same_v<T, jbyte>) {
219 return CTString("B");
220 } else if constexpr (std::is_same_v<T, jchar>) {
221 return CTString("C");
222 } else if constexpr (std::is_same_v<T, char>) {
223 return CTString("C");
224 } else if constexpr (std::is_same_v<T, jshort>) {
225 return CTString("S");
226 } else if constexpr (std::is_same_v<T, short>) {
227 return CTString("S");
228 } else if constexpr (std::is_same_v<T, jint>) {
229 return CTString("I");
230 } else if constexpr (std::is_same_v<T, int>) {
231 return CTString("I");
232 } else if constexpr (std::is_same_v<T, uint>) {
233 return CTString("I");
234 } else if constexpr (std::is_same_v<T, jlong>) {
235 return CTString("J");
236 } else if constexpr (std::is_same_v<T, quint64>) {
237 return CTString("J");
238 } else if constexpr (std::is_same_v<T, jfloat>) {
239 return CTString("F");
240 } else if constexpr (std::is_same_v<T, float>) {
241 return CTString("F");
242 } else if constexpr (std::is_same_v<T, jdouble>) {
243 return CTString("D");
244 } else if constexpr (std::is_same_v<T, double>) {
245 return CTString("D");
246 } else if constexpr (std::is_same_v<T, void>) {
247 return CTString("V");
248 } else if constexpr (std::is_enum_v<T>) {
249 return Traits<std::underlying_type_t<T>>::signature();
250 }
251 // else: return void -> not implemented
252 }
253
254 template <typename U = T>
255 static auto convertToJni(JNIEnv *, U &&value)
256 {
257 return std::forward<U>(value);
258 }
259 static auto convertFromJni(QJniObject &&object)
260 {
261 return std::move(object);
262 }
263};
264
265template <typename Have, typename Want>
266static constexpr bool sameTypeForJni = (QtJniTypes::Traits<Have>::signature()
267 == QtJniTypes::Traits<Want>::signature())
268 && (sizeof(Have) == sizeof(Want));
269
270template <typename, typename = void>
271struct Caller
272{};
273
274#define MAKE_CALLER(Type, Method) template
275 <typename T> struct
276 Caller<T, std::enable_if_t<sameTypeForJni<T, Type>>> \
277{
278 static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, va_list args)
279 {
280 res = T(env->Call##Method##MethodV(obj, id, args));
281 }
282 static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz, jmethodID id, va_list args)
283 {
284 res = T(env->CallStatic##Method##MethodV(clazz, id, args));
285 }
286 static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id)
287 {
288 res = T(env->Get##Method##Field(obj, id));
289 }
290 static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, jfieldID id)
291 {
292 res = T(env->GetStatic##Method##Field(clazz, id));
293 }
294 static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, T value)
295 {
296 env->Set##Method##Field(obj, id, static_cast<Type>(value));
297 }
298 static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, T value)
299 {
300 env->SetStatic##Method##Field(clazz, id, static_cast<Type>(value));
301 } \
302}
303
304MAKE_CALLER(jboolean, Boolean);
305MAKE_CALLER(jbyte, Byte);
306MAKE_CALLER(jchar, Char);
307MAKE_CALLER(jshort, Short);
308MAKE_CALLER(jint, Int);
309MAKE_CALLER(jlong, Long);
310MAKE_CALLER(jfloat, Float);
311MAKE_CALLER(jdouble, Double);
312
313#undef MAKE_CALLER
314
315template<typename T>
316static constexpr bool isPrimitiveType()
317{
318 return Traits<T>::signature().size() == 2;
319}
320
321template<typename T>
322static constexpr bool isArrayType()
323{
324 constexpr auto signature = Traits<T>::signature();
325 return signature.startsWith('[') && signature.size() > 2;
326}
327
328template<typename T>
329static constexpr bool isObjectType()
330{
331 if constexpr (std::is_convertible_v<T, jobject>) {
332 return true;
333 } else {
334 constexpr auto signature = Traits<T>::signature();
335 return (signature.startsWith('L') && signature.endsWith(';')) || isArrayType<T>();
336 }
337}
338
339template<typename T>
340static constexpr void assertObjectType()
341{
342 static_assert(isObjectType<T>(),
343 "Type needs to be a JNI object type (convertible to jobject, or with "
344 "an object type signature registered)!");
345}
346
347// A set of types is valid if Traits::signature is implemented for all of them
348template<typename ...Types>
349constexpr bool ValidSignatureTypesDetail = !std::disjunction<std::is_same<
350 decltype(Traits<Types>::signature()),
351 void>...,
352 IsStringType<Types>...>::value;
353template<typename ...Types>
354using IfValidSignatureTypes = std::enable_if_t<
355 ValidSignatureTypesDetail<q20::remove_cvref_t<Types>...>, bool>;
356
357template<typename Type>
358constexpr bool ValidFieldTypeDetail = isObjectType<Type>() || isPrimitiveType<Type>();
359template<typename Type>
360using IfValidFieldType = std::enable_if_t<
361 ValidFieldTypeDetail<q20::remove_cvref_t<Type>>, bool>;
362
363
364template<typename R, typename ...Args, IfValidSignatureTypes<R, Args...> = true>
365static constexpr auto methodSignature()
366{
367 return (CTString("(") +
368 ... + Traits<q20::remove_cvref_t<Args>>::signature())
369 + CTString(")")
370 + Traits<R>::signature();
371}
372
373template<typename T, IfValidSignatureTypes<T> = true>
374static constexpr auto fieldSignature()
375{
376 return QtJniTypes::Traits<T>::signature();
377}
378
379template<typename ...Args, IfValidSignatureTypes<Args...> = true>
380static constexpr auto constructorSignature()
381{
382 return methodSignature<void, Args...>();
383}
384
385template<typename Ret, typename ...Args, IfValidSignatureTypes<Ret, Args...> = true>
386static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...))
387{
388 return methodSignature<Ret, Args...>();
389}
390
391template<typename Ret, typename ...Args, IfValidSignatureTypes<Ret, Args...> = true>
392static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...))
393{
394 return methodSignature<Ret, Args...>();
395}
396
397} // namespace QtJniTypes
398
399QT_END_NAMESPACE
400
401#endif
402
403#endif // QJNITYPES_IMPL_H