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