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