summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2023-09-12 17:48:40 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2023-09-20 17:16:28 +0200
commit1de8cb8f913f7a6d4b802c04aff5a21852168ec0 (patch)
treeefa3332b6db0bf558dc4d936ee15a8ec417de668
parent7a27609d73da8725b6a894d9f2911a0eece13bc1 (diff)
JNI: move JNI type macros into separate header
This removes the dependency from QJniObject to QtJniTypes, and allows us to add more useful helpers to types declared through the macros, as we can rely on QJniObject being fully declared. Note: this is all undocumented API, so fine to change even though it currently lives in a public header. Change-Id: I07478ecb80ae166d619a09aed6820f680afed31b Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
-rw-r--r--src/corelib/CMakeLists.txt2
-rw-r--r--src/corelib/kernel/qjnienvironment.h2
-rw-r--r--src/corelib/kernel/qjniobject.h2
-rw-r--r--src/corelib/kernel/qjnitypes.h321
-rw-r--r--src/corelib/kernel/qjnitypes_impl.h329
5 files changed, 339 insertions, 317 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index c638266256..8578afc61d 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -999,7 +999,7 @@ qt_internal_extend_target(Core CONDITION ANDROID
SOURCES
io/qstandardpaths_android.cpp
io/qstorageinfo_linux.cpp
- kernel/qjnitypes.h
+ kernel/qjnitypes.h kernel/qjnitypes_impl.h
kernel/qjnienvironment.cpp kernel/qjnienvironment.h
kernel/qjniobject.cpp kernel/qjniobject.h
kernel/qjnihelpers.cpp kernel/qjnihelpers_p.h
diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h
index f7ffa836c2..2121ceb6fe 100644
--- a/src/corelib/kernel/qjnienvironment.h
+++ b/src/corelib/kernel/qjnienvironment.h
@@ -8,7 +8,7 @@
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
-#include <QtCore/qjnitypes.h>
+#include <QtCore/qjnitypes_impl.h>
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h
index 75c896a728..de73676630 100644
--- a/src/corelib/kernel/qjniobject.h
+++ b/src/corelib/kernel/qjniobject.h
@@ -9,7 +9,6 @@
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
#include <QtCore/qjnienvironment.h>
-#include <QtCore/qjnitypes.h>
QT_BEGIN_NAMESPACE
@@ -42,7 +41,6 @@ public:
std::forward<Args>(args)...)
{}
QJniObject(jobject globalRef);
- inline QJniObject(QtJniTypes::Object wrapper) noexcept : QJniObject(jobject(wrapper)) {}
~QJniObject();
template<typename Class, typename ...Args>
diff --git a/src/corelib/kernel/qjnitypes.h b/src/corelib/kernel/qjnitypes.h
index 067504c41d..72d6f2783c 100644
--- a/src/corelib/kernel/qjnitypes.h
+++ b/src/corelib/kernel/qjnitypes.h
@@ -4,330 +4,25 @@
#ifndef QJNITYPES_H
#define QJNITYPES_H
-#include <QtCore/qglobal.h>
-#include <QtCore/q20type_traits.h>
-
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
-#include <jni.h>
+
+#include <QtCore/qjnitypes_impl.h>
+#include <QtCore/qjniobject.h>
QT_BEGIN_NAMESPACE
namespace QtJniTypes
{
-
-// a constexpr type for string literals of any character width, aware of the length
-// of the string.
-template<size_t N_WITH_NULL, typename BaseType = char>
-struct String
-{
- BaseType m_data[N_WITH_NULL] = {};
-
- constexpr String() noexcept {}
- // Can be instantiated (only) with a string literal
- constexpr explicit String(const BaseType (&data)[N_WITH_NULL]) noexcept
- {
- for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
- m_data[i] = data[i];
- }
-
- constexpr BaseType at(size_t i) const { return m_data[i]; }
- constexpr BaseType operator[](size_t i) const { return at(i); }
- static constexpr size_t size() noexcept { return N_WITH_NULL; }
- constexpr operator const BaseType *() const noexcept { return m_data; }
- constexpr const BaseType *data() const noexcept { return m_data; }
- template<size_t N2_WITH_NULL>
- constexpr bool startsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
- {
- if constexpr (N2_WITH_NULL > N_WITH_NULL) {
- return false;
- } else {
- for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) {
- if (m_data[i] != lit[i])
- return false;
- }
- }
- return true;
- }
- constexpr bool startsWith(BaseType c) const noexcept
- {
- return N_WITH_NULL > 1 && m_data[0] == c;
- }
- template<size_t N2_WITH_NULL>
- constexpr bool endsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
- {
- if constexpr (N2_WITH_NULL > N_WITH_NULL) {
- return false;
- } else {
- for (size_t i = 0; i < N2_WITH_NULL; ++i) {
- if (m_data[N_WITH_NULL - i - 1] != lit[N2_WITH_NULL - i - 1])
- return false;
- }
- }
- return true;
- }
- constexpr bool endsWith(BaseType c) const noexcept
- {
- return N_WITH_NULL > 1 && m_data[N_WITH_NULL - 2] == c;
- }
-
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator==(const String<N_WITH_NULL> &lhs,
- const String<N2_WITH_NULL> &rhs) noexcept
- {
- if constexpr (N_WITH_NULL != N2_WITH_NULL) {
- return false;
- } else {
- for (size_t i = 0; i < N_WITH_NULL - 1; ++i) {
- if (lhs.at(i) != rhs.at(i))
- return false;
- }
- }
- return true;
- }
-
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator!=(const String<N_WITH_NULL> &lhs,
- const String<N2_WITH_NULL> &rhs) noexcept
- {
- return !operator==(lhs, rhs);
- }
-
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator==(const String<N_WITH_NULL> &lhs,
- const BaseType (&rhs)[N2_WITH_NULL]) noexcept
- {
- return operator==(lhs, String<N2_WITH_NULL>(rhs));
- }
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL],
- const String<N_WITH_NULL> &rhs) noexcept
- {
- return operator==(String<N2_WITH_NULL>(lhs), rhs);
- }
-
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator!=(const String<N_WITH_NULL> &lhs,
- const BaseType (&rhs)[N2_WITH_NULL]) noexcept
- {
- return operator!=(lhs, String<N2_WITH_NULL>(rhs));
- }
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL],
- const String<N_WITH_NULL> &rhs) noexcept
- {
- return operator!=(String<N2_WITH_NULL>(lhs), rhs);
- }
-
- template<size_t N2_WITH_NULL>
- friend inline constexpr auto operator+(const String<N_WITH_NULL> &lhs,
- const String<N2_WITH_NULL> &rhs) noexcept
- {
- char data[N_WITH_NULL + N2_WITH_NULL - 1] = {};
- for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
- data[i] = lhs[i];
- for (size_t i = 0; i < N2_WITH_NULL - 1; ++i)
- data[N_WITH_NULL - 1 + i] = rhs[i];
- return String<N_WITH_NULL + N2_WITH_NULL - 1>(data);
- }
-};
-
-
-// Helper types that allow us to disable variadic overloads that would conflict
-// with overloads that take a const char*.
-template<typename T, size_t N = 0> struct IsStringType : std::false_type {};
-template<> struct IsStringType<const char*, 0> : std::true_type {};
-template<size_t N> struct IsStringType<String<N>> : std::true_type {};
-template<size_t N> struct IsStringType<const char[N]> : std::true_type {};
-
-template<bool flag = false>
-static void staticAssertTypeMismatch()
-{
- static_assert(flag, "The used type is not supported by this template call. "
- "Use a JNI based type instead.");
-}
-
-template<typename T>
-constexpr auto typeSignature()
-{
- if constexpr (std::is_array_v<T>) {
- using UnderlyingType = typename std::remove_extent_t<T>;
- static_assert(!std::is_array_v<UnderlyingType>,
- "typeSignature() does not handle multi-dimensional arrays");
- return String("[") + typeSignature<UnderlyingType>();
- } else if constexpr (std::is_same_v<T, jobject>) {
- return String("Ljava/lang/Object;");
- } else if constexpr (std::is_same_v<T, jclass>) {
- return String("Ljava/lang/Class;");
- } else if constexpr (std::is_same_v<T, jstring>) {
- return String("Ljava/lang/String;");
- } else if constexpr (std::is_same_v<T, jobjectArray>) {
- return String("[Ljava/lang/Object;");
- } else if constexpr (std::is_same_v<T, jthrowable>) {
- return String("Ljava/lang/Throwable;");
- } else if constexpr (std::is_same_v<T, jbooleanArray>) {
- return String("[Z");
- } else if constexpr (std::is_same_v<T, jbyteArray>) {
- return String("[B");
- } else if constexpr (std::is_same_v<T, jshortArray>) {
- return String("[S");
- } else if constexpr (std::is_same_v<T, jintArray>) {
- return String("[I");
- } else if constexpr (std::is_same_v<T, jlongArray>) {
- return String("[J");
- } else if constexpr (std::is_same_v<T, jfloatArray>) {
- return String("[F");
- } else if constexpr (std::is_same_v<T, jdoubleArray>) {
- return String("[D");
- } else if constexpr (std::is_same_v<T, jcharArray>) {
- return String("[C");
- } else if constexpr (std::is_same_v<T, jboolean>) {
- return String("Z");
- } else if constexpr (std::is_same_v<T, bool>) {
- return String("Z");
- } else if constexpr (std::is_same_v<T, jbyte>) {
- return String("B");
- } else if constexpr (std::is_same_v<T, jchar>) {
- return String("C");
- } else if constexpr (std::is_same_v<T, char>) {
- return String("C");
- } else if constexpr (std::is_same_v<T, jshort>) {
- return String("S");
- } else if constexpr (std::is_same_v<T, short>) {
- return String("S");
- } else if constexpr (std::is_same_v<T, jint>) {
- return String("I");
- } else if constexpr (std::is_same_v<T, int>) {
- return String("I");
- } else if constexpr (std::is_same_v<T, uint>) {
- return String("I");
- } else if constexpr (std::is_same_v<T, jlong>) {
- return String("J");
- } else if constexpr (std::is_same_v<T, long>) {
- return String("J");
- } else if constexpr (std::is_same_v<T, jfloat>) {
- return String("F");
- } else if constexpr (std::is_same_v<T, float>) {
- return String("F");
- } else if constexpr (std::is_same_v<T, jdouble>) {
- return String("D");
- } else if constexpr (std::is_same_v<T, double>) {
- return String("D");
- } else if constexpr (std::is_same_v<T, void>) {
- return String("V");
- }
-
- // else: The return type becomes void, indicating that the typeSignature
- // template is not implemented for the respective type. We use this to
- // detect invalid types in the ValidSignatureTypes and ValidFieldType
- // predicates below.
-}
-
-template<bool flag = false>
-static void staticAssertClassNotRegistered()
-{
- static_assert(flag, "Class not registered, use Q_DECLARE_JNI_CLASS");
-}
-
-template<typename T>
-constexpr auto className()
-{
- if constexpr (std::is_same_v<T, jstring>)
- return String("java/lang/String");
- else
- staticAssertClassNotRegistered();
-}
-
-template<typename T>
-static constexpr bool isPrimitiveType()
-{
- return typeSignature<T>().size() == 2;
-}
-
-template<typename T>
-static constexpr bool isObjectType()
-{
- if constexpr (std::is_convertible_v<T, jobject>) {
- return true;
- } else {
- constexpr auto signature = typeSignature<T>();
- return (signature.startsWith('L') || signature.startsWith('['))
- && signature.endsWith(';');
- }
-}
-
-template<typename T>
-static constexpr bool isArrayType()
-{
- constexpr auto signature = typeSignature<T>();
- return signature.startsWith('[');
-}
-
-template<typename T>
-static constexpr void assertObjectType()
-{
- static_assert(isObjectType<T>(),
- "Type needs to be a JNI object type (convertible to jobject, or with "
- "an object type signature registered)!");
-}
-
-// A set of types is valid if typeSignature is implemented for all of them
-template<typename ...Types>
-constexpr bool ValidSignatureTypesDetail = !std::disjunction<std::is_same<
- decltype(QtJniTypes::typeSignature<Types>()),
- void>...,
- IsStringType<Types>...>::value;
-template<typename ...Types>
-using ValidSignatureTypes = std::enable_if_t<
- ValidSignatureTypesDetail<q20::remove_cvref_t<Types>...>, bool>;
-
-template<typename Type>
-constexpr bool ValidFieldTypeDetail = isObjectType<Type>() || isPrimitiveType<Type>();
-template<typename Type>
-using ValidFieldType = std::enable_if_t<
- ValidFieldTypeDetail<q20::remove_cvref_t<Type>>, bool>;
-
-
-template<typename R, typename ...Args, ValidSignatureTypes<R, Args...> = true>
-static constexpr auto methodSignature()
-{
- return (String("(") +
- ... + typeSignature<q20::remove_cvref_t<Args>>())
- + String(")")
- + typeSignature<R>();
-}
-
-template<typename T, ValidSignatureTypes<T> = true>
-static constexpr auto fieldSignature()
-{
- return QtJniTypes::typeSignature<T>();
-}
-
-template<typename ...Args, ValidSignatureTypes<Args...> = true>
-static constexpr auto constructorSignature()
-{
- return methodSignature<void, Args...>();
-}
-
-template<typename Ret, typename ...Args, ValidSignatureTypes<Ret, Args...> = true>
-static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...))
-{
- return methodSignature<Ret, Args...>();
-}
-
-template<typename Ret, typename ...Args, ValidSignatureTypes<Ret, Args...> = true>
-static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...))
-{
- return methodSignature<Ret, Args...>();
-}
-
// A generic thin wrapper around jobject, convertible to jobject.
// We need this as a baseclass so that QJniObject can be implicitly
-// constructed from the various subclasses - we can't provide an
-// operator QJniObject() here as the class is not declared.
+// constructed from the various subclasses. We can also pass instances
+// of this type (or of any of the generated subclasses) as if it was
+// a jobject.
struct Object
{
jobject _object;
constexpr operator jobject() const { return _object; }
+ operator QJniObject() const { return QJniObject(_object); }
};
} // namespace QtJniTypes
@@ -402,6 +97,6 @@ static const JNINativeMethod Method##_method = { \
QT_END_NAMESPACE
-#endif
+#endif // defined(Q_QDOC) || defined(Q_OS_ANDROID)
#endif // QJNITYPES_H
diff --git a/src/corelib/kernel/qjnitypes_impl.h b/src/corelib/kernel/qjnitypes_impl.h
new file mode 100644
index 0000000000..a7a3a30b2a
--- /dev/null
+++ b/src/corelib/kernel/qjnitypes_impl.h
@@ -0,0 +1,329 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QJNITYPES_IMPL_H
+#define QJNITYPES_IMPL_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/q20type_traits.h>
+
+#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
+#include <jni.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtJniTypes
+{
+
+// a constexpr type for string literals of any character width, aware of the length
+// of the string.
+template<size_t N_WITH_NULL, typename BaseType = char>
+struct String
+{
+ BaseType m_data[N_WITH_NULL] = {};
+
+ constexpr String() noexcept {}
+ // Can be instantiated (only) with a string literal
+ constexpr explicit String(const BaseType (&data)[N_WITH_NULL]) noexcept
+ {
+ for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
+ m_data[i] = data[i];
+ }
+
+ constexpr BaseType at(size_t i) const { return m_data[i]; }
+ constexpr BaseType operator[](size_t i) const { return at(i); }
+ static constexpr size_t size() noexcept { return N_WITH_NULL; }
+ constexpr operator const BaseType *() const noexcept { return m_data; }
+ constexpr const BaseType *data() const noexcept { return m_data; }
+ template<size_t N2_WITH_NULL>
+ constexpr bool startsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
+ {
+ if constexpr (N2_WITH_NULL > N_WITH_NULL) {
+ return false;
+ } else {
+ for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) {
+ if (m_data[i] != lit[i])
+ return false;
+ }
+ }
+ return true;
+ }
+ constexpr bool startsWith(BaseType c) const noexcept
+ {
+ return N_WITH_NULL > 1 && m_data[0] == c;
+ }
+ template<size_t N2_WITH_NULL>
+ constexpr bool endsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
+ {
+ if constexpr (N2_WITH_NULL > N_WITH_NULL) {
+ return false;
+ } else {
+ for (size_t i = 0; i < N2_WITH_NULL; ++i) {
+ if (m_data[N_WITH_NULL - i - 1] != lit[N2_WITH_NULL - i - 1])
+ return false;
+ }
+ }
+ return true;
+ }
+ constexpr bool endsWith(BaseType c) const noexcept
+ {
+ return N_WITH_NULL > 1 && m_data[N_WITH_NULL - 2] == c;
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator==(const String<N_WITH_NULL> &lhs,
+ const String<N2_WITH_NULL> &rhs) noexcept
+ {
+ if constexpr (N_WITH_NULL != N2_WITH_NULL) {
+ return false;
+ } else {
+ for (size_t i = 0; i < N_WITH_NULL - 1; ++i) {
+ if (lhs.at(i) != rhs.at(i))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator!=(const String<N_WITH_NULL> &lhs,
+ const String<N2_WITH_NULL> &rhs) noexcept
+ {
+ return !operator==(lhs, rhs);
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator==(const String<N_WITH_NULL> &lhs,
+ const BaseType (&rhs)[N2_WITH_NULL]) noexcept
+ {
+ return operator==(lhs, String<N2_WITH_NULL>(rhs));
+ }
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL],
+ const String<N_WITH_NULL> &rhs) noexcept
+ {
+ return operator==(String<N2_WITH_NULL>(lhs), rhs);
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator!=(const String<N_WITH_NULL> &lhs,
+ const BaseType (&rhs)[N2_WITH_NULL]) noexcept
+ {
+ return operator!=(lhs, String<N2_WITH_NULL>(rhs));
+ }
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL],
+ const String<N_WITH_NULL> &rhs) noexcept
+ {
+ return operator!=(String<N2_WITH_NULL>(lhs), rhs);
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr auto operator+(const String<N_WITH_NULL> &lhs,
+ const String<N2_WITH_NULL> &rhs) noexcept
+ {
+ char data[N_WITH_NULL + N2_WITH_NULL - 1] = {};
+ for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
+ data[i] = lhs[i];
+ for (size_t i = 0; i < N2_WITH_NULL - 1; ++i)
+ data[N_WITH_NULL - 1 + i] = rhs[i];
+ return String<N_WITH_NULL + N2_WITH_NULL - 1>(data);
+ }
+};
+
+
+// Helper types that allow us to disable variadic overloads that would conflict
+// with overloads that take a const char*.
+template<typename T, size_t N = 0> struct IsStringType : std::false_type {};
+template<> struct IsStringType<const char*, 0> : std::true_type {};
+template<size_t N> struct IsStringType<String<N>> : std::true_type {};
+template<size_t N> struct IsStringType<const char[N]> : std::true_type {};
+
+template<bool flag = false>
+static void staticAssertTypeMismatch()
+{
+ static_assert(flag, "The used type is not supported by this template call. "
+ "Use a JNI based type instead.");
+}
+
+template<typename T>
+constexpr auto typeSignature()
+{
+ if constexpr (std::is_array_v<T>) {
+ using UnderlyingType = typename std::remove_extent_t<T>;
+ static_assert(!std::is_array_v<UnderlyingType>,
+ "typeSignature() does not handle multi-dimensional arrays");
+ return String("[") + typeSignature<UnderlyingType>();
+ } else if constexpr (std::is_same_v<T, jobject>) {
+ return String("Ljava/lang/Object;");
+ } else if constexpr (std::is_same_v<T, jclass>) {
+ return String("Ljava/lang/Class;");
+ } else if constexpr (std::is_same_v<T, jstring>) {
+ return String("Ljava/lang/String;");
+ } else if constexpr (std::is_same_v<T, jobjectArray>) {
+ return String("[Ljava/lang/Object;");
+ } else if constexpr (std::is_same_v<T, jthrowable>) {
+ return String("Ljava/lang/Throwable;");
+ } else if constexpr (std::is_same_v<T, jbooleanArray>) {
+ return String("[Z");
+ } else if constexpr (std::is_same_v<T, jbyteArray>) {
+ return String("[B");
+ } else if constexpr (std::is_same_v<T, jshortArray>) {
+ return String("[S");
+ } else if constexpr (std::is_same_v<T, jintArray>) {
+ return String("[I");
+ } else if constexpr (std::is_same_v<T, jlongArray>) {
+ return String("[J");
+ } else if constexpr (std::is_same_v<T, jfloatArray>) {
+ return String("[F");
+ } else if constexpr (std::is_same_v<T, jdoubleArray>) {
+ return String("[D");
+ } else if constexpr (std::is_same_v<T, jcharArray>) {
+ return String("[C");
+ } else if constexpr (std::is_same_v<T, jboolean>) {
+ return String("Z");
+ } else if constexpr (std::is_same_v<T, bool>) {
+ return String("Z");
+ } else if constexpr (std::is_same_v<T, jbyte>) {
+ return String("B");
+ } else if constexpr (std::is_same_v<T, jchar>) {
+ return String("C");
+ } else if constexpr (std::is_same_v<T, char>) {
+ return String("C");
+ } else if constexpr (std::is_same_v<T, jshort>) {
+ return String("S");
+ } else if constexpr (std::is_same_v<T, short>) {
+ return String("S");
+ } else if constexpr (std::is_same_v<T, jint>) {
+ return String("I");
+ } else if constexpr (std::is_same_v<T, int>) {
+ return String("I");
+ } else if constexpr (std::is_same_v<T, uint>) {
+ return String("I");
+ } else if constexpr (std::is_same_v<T, jlong>) {
+ return String("J");
+ } else if constexpr (std::is_same_v<T, long>) {
+ return String("J");
+ } else if constexpr (std::is_same_v<T, jfloat>) {
+ return String("F");
+ } else if constexpr (std::is_same_v<T, float>) {
+ return String("F");
+ } else if constexpr (std::is_same_v<T, jdouble>) {
+ return String("D");
+ } else if constexpr (std::is_same_v<T, double>) {
+ return String("D");
+ } else if constexpr (std::is_same_v<T, void>) {
+ return String("V");
+ }
+
+ // else: The return type becomes void, indicating that the typeSignature
+ // template is not implemented for the respective type. We use this to
+ // detect invalid types in the ValidSignatureTypes and ValidFieldType
+ // predicates below.
+}
+
+template<bool flag = false>
+static void staticAssertClassNotRegistered()
+{
+ static_assert(flag, "Class not registered, use Q_DECLARE_JNI_CLASS");
+}
+
+template<typename T>
+constexpr auto className()
+{
+ if constexpr (std::is_same_v<T, jstring>)
+ return String("java/lang/String");
+ else
+ staticAssertClassNotRegistered();
+}
+
+template<typename T>
+static constexpr bool isPrimitiveType()
+{
+ return typeSignature<T>().size() == 2;
+}
+
+template<typename T>
+static constexpr bool isObjectType()
+{
+ if constexpr (std::is_convertible_v<T, jobject>) {
+ return true;
+ } else {
+ constexpr auto signature = typeSignature<T>();
+ return (signature.startsWith('L') || signature.startsWith('['))
+ && signature.endsWith(';');
+ }
+}
+
+template<typename T>
+static constexpr bool isArrayType()
+{
+ constexpr auto signature = typeSignature<T>();
+ return signature.startsWith('[');
+}
+
+template<typename T>
+static constexpr void assertObjectType()
+{
+ static_assert(isObjectType<T>(),
+ "Type needs to be a JNI object type (convertible to jobject, or with "
+ "an object type signature registered)!");
+}
+
+// A set of types is valid if typeSignature is implemented for all of them
+template<typename ...Types>
+constexpr bool ValidSignatureTypesDetail = !std::disjunction<std::is_same<
+ decltype(QtJniTypes::typeSignature<Types>()),
+ void>...,
+ IsStringType<Types>...>::value;
+template<typename ...Types>
+using ValidSignatureTypes = std::enable_if_t<
+ ValidSignatureTypesDetail<q20::remove_cvref_t<Types>...>, bool>;
+
+template<typename Type>
+constexpr bool ValidFieldTypeDetail = isObjectType<Type>() || isPrimitiveType<Type>();
+template<typename Type>
+using ValidFieldType = std::enable_if_t<
+ ValidFieldTypeDetail<q20::remove_cvref_t<Type>>, bool>;
+
+
+template<typename R, typename ...Args, ValidSignatureTypes<R, Args...> = true>
+static constexpr auto methodSignature()
+{
+ return (String("(") +
+ ... + typeSignature<q20::remove_cvref_t<Args>>())
+ + String(")")
+ + typeSignature<R>();
+}
+
+template<typename T, ValidSignatureTypes<T> = true>
+static constexpr auto fieldSignature()
+{
+ return QtJniTypes::typeSignature<T>();
+}
+
+template<typename ...Args, ValidSignatureTypes<Args...> = true>
+static constexpr auto constructorSignature()
+{
+ return methodSignature<void, Args...>();
+}
+
+template<typename Ret, typename ...Args, ValidSignatureTypes<Ret, Args...> = true>
+static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...))
+{
+ return methodSignature<Ret, Args...>();
+}
+
+template<typename Ret, typename ...Args, ValidSignatureTypes<Ret, Args...> = true>
+static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...))
+{
+ return methodSignature<Ret, Args...>();
+}
+
+} // namespace QtJniTypes
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif // QJNITYPES_IMPL_H