Add variadic template overloads for QJniObject/Environment methods

This allows the compiler to deduce the template arguments based on the
provided method parameters, which we can then pass to the methodSignature
and fieldSignature helpers to generate the signature string completely at
compile time.

Since we can't partially specialize template member functions, replace
the specializations for void methods with compile-time-if branches in
the general templates.

This variadic template now prevents implicit conversion from the
LiteralStorage types to const char* signatures, so catch the case where
such a type ends up in the parameter list.

Due to overload resolution rules for constructors, we need to explicitly
disable the constructor if any of the arguments is a string literal type,
as we have to keep the old C-style variadic function working for such
calls.

Add variations that use the variadic templates to the unit tests.

Change-Id: I8734664b38bae932369462330a9a03302254c33c
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Volker Hilsheimer 2022-04-23 16:43:35 +02:00
parent b9c55b5b9e
commit 601dbd6499
6 changed files with 460 additions and 217 deletions

View File

@ -231,6 +231,16 @@ jmethodID QJniEnvironment::findMethod(jclass clazz, const char *methodName, cons
return nullptr;
}
/*!
\fn template<typename ...Args> jmethodId QJniEnvironment::findMethod(jclass clazz, const char *methodName)
\since 6.4
Searches for an instance method of a class \a clazz. The method is specified
by its \a methodName, the signature is deduced from the template parameters.
Returns the method ID or \c nullptr if the method is not found.
*/
/*!
Searches for a static method of a class \a clazz. The method is specified
by its \a methodName and \a signature.
@ -265,9 +275,28 @@ jmethodID QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName
return nullptr;
}
/*!
\fn template<typename ...Args> jmethodId QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName)
\since 6.4
Searches for an instance method of a class \a clazz. The method is specified
by its \a methodName, the signature is deduced from the template parameters.
Returns the method ID or \c nullptr if the method is not found.
\code
QJniEnvironment env;
jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass");
jmethodID methodId = env.findStaticMethod<void, jstring>(javaClass, "staticJavaMethod");
QJniObject javaMessage = QJniObject::fromString("findStaticMethod example");
QJniObject::callStaticMethod<void>(javaClass,
methodId,
javaMessage.object<jstring>());
\endcode
*/
/*!
Searches for an member field of a class \a clazz. The field is specified
Searches for a member field of a class \a clazz. The field is specified
by its \a fieldName and \a signature.
Returns the field ID or \c nullptr if the field is not found.
@ -288,6 +317,16 @@ jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName, const c
return nullptr;
}
/*!
\fn template<typename T> jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName)
\since 6.4
Searches for a member field of a class \a clazz. The field is specified
by its \a fieldName. The signature of the field is deduced from the template parameter.
Returns the field ID or \c nullptr if the field is not found.
*/
/*!
Searches for a static field of a class \a clazz. The field is specified
by its \a fieldName and \a signature.
@ -310,6 +349,16 @@ jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName, c
return nullptr;
}
/*!
\fn template<typename T> jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName)
\since 6.4
Searches for a static field of a class \a clazz. The field is specified
by its \a fieldName. The signature of the field is deduced from the template parameter.
Returns the field ID or \c nullptr if the field is not found.
*/
/*!
\fn JavaVM *QJniEnvironment::javaVM()

View File

@ -44,6 +44,7 @@
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
#include <QtCore/qjnitypes.h>
QT_BEGIN_NAMESPACE
@ -60,9 +61,29 @@ public:
JNIEnv *jniEnv() const;
jclass findClass(const char *className);
jmethodID findMethod(jclass clazz, const char *methodName, const char *signature);
template<typename ...Args>
jmethodID findMethod(jclass clazz, const char *methodName) {
constexpr auto signature = QtJniTypes::methodSignature<Args...>();
return findMethod(clazz, methodName, signature.data());
}
jmethodID findStaticMethod(jclass clazz, const char *methodName, const char *signature);
template<typename ...Args>
jmethodID findStaticMethod(jclass clazz, const char *methodName) {
constexpr auto signature = QtJniTypes::methodSignature<Args...>();
return findStaticMethod(clazz, methodName, signature.data());
}
jfieldID findField(jclass clazz, const char *fieldName, const char *signature);
template<typename T>
jfieldID findField(jclass clazz, const char *fieldName) {
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return findField(clazz, fieldName, signature.data());
}
jfieldID findStaticField(jclass clazz, const char *fieldName, const char *signature);
template<typename T>
jfieldID findStaticField(jclass clazz, const char *fieldName) {
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return findStaticField(clazz, fieldName, signature.data());
}
static JavaVM *javaVM();
bool registerNativeMethods(const char *className, const JNINativeMethod methods[], int size);
bool registerNativeMethods(jclass clazz, const JNINativeMethod methods[], int size);

View File

@ -76,55 +76,77 @@ using namespace Qt::StringLiterals;
\section1 Method Signatures
For functions that take no arguments, QJniObject provides convenience functions that will use
the correct signature based on the provided template type. For example:
QJniObject provides convenience functions that will use the correct signature based on the
provided template types. For functions that only return and take \l {JNI types}, the
signature can be generate at compile time:
\code
jint x = QJniObject::callMethod<jint>("getSize");
QJniObject::callMethod<void>("touch");
jint ret = jString1.callMethod<jint>("compareToIgnoreCase", jString2.object<jstring>());
\endcode
In other cases you will need to supply the signature yourself, and it is important that the
signature matches the function you want to call. The signature structure is
\c "(ArgumentsTypes)ReturnType". Array types in the signature must have the \c {[} prefix,
and the fully-qualified \c Object type names must have the \c L prefix and the \c ; suffix.
These functions are variadic templates, and the compiler will deduce the template arguments
from the actual argument types. In many situations, only the return type needs to be provided
explicitly.
The example below demonstrates how to call two different static functions:
For functions that take other argument types, you need to supply the signature yourself. It is
important that the signature matches the function you want to call. The example below
demonstrates how to call different static functions:
\code
// Java class
package org.qtproject.qt;
class TestClass
{
static String fromNumber(int x) { ... }
static String[] stringArray(String s1, String s2) { ... }
static TestClass create() { ... }
static String fromNumber(int x) { ... }
static String[] stringArray(String s1, String s2) { ... }
}
\endcode
The signature for the first function is \c {"(I)Ljava/lang/String;"}:
The signature structure is \c "(ArgumentsTypes)ReturnType". Array types in the signature
must have the \c {[} prefix, and the fully-qualified \c Object type names must have the
\c L prefix and the \c ; suffix. The signature for the \c create function is
\c {"()Lorg/qtproject/qt/TestClass;}. The signatures for the second and third functions
are \c {"(I)Ljava/lang/String;"} and
\c {"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"}, respectively.
We can call the \c create() function like this:
\code
// C++ code
QJniObject testClass = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
"create",
"()Lorg/qtproject/qt/TestClass;");
\endcode
For the second and third function we can rely on QJniObject's template methods to create
the implicit signature string, but we can also pass the signature string explicitly:
\code
// C++ code
QJniObject stringNumber = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
"fromNumber"
"(I)Ljava/lang/String;",
10);
"fromNumber",
"(I)Ljava/lang/String;", 10);
\endcode
and the signature for the second function is
\c {"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"}:
For the implicit signature creation to work we need to specify the return type explicitly:
\code
// C++ code
QJniObject string1 = QJniObject::fromString("String1");
QJniObject string2 = QJniObject::fromString("String2");
QJniObject stringArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
QJniObject stringArray = QJniObject::callStaticObjectMethod<jstringArray>(
"org/qtproject/qt/TestClass",
"stringArray"
"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
string1.object<jstring>(),
string2.object<jstring>());
\endcode
Note that while he first template parameter specifies the return type of the Java
function, the method will still return a QJniObject.
\section1 Handling Java Exception
After calling Java functions that might throw exceptions, it is important
@ -403,9 +425,12 @@ jmethodID QJniObject::getMethodID(JNIEnv *env,
return id;
}
void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const
void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, ...) const
{
va_list args;
va_start(args, id);
env->CallVoidMethodV(d->m_jobject, id, args);
va_end(args);
}
jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
@ -620,6 +645,22 @@ QJniObject::QJniObject(const char *className, const char *signature, ...)
}
}
/*!
\fn template<typename ...Args> QJniObject::QJniObject(const char *className, Args &&...args)
\since 6.4
Constructs a new JNI object by calling the constructor of \a className with
the arguments \a args. This constructor is only available if all \a args are
known \l {JNI Types}.
\code
QJniEnvironment env;
char* str = "Hello";
jstring myJStringArg = env->NewStringUTF(str);
QJniObject myNewJavaString("java/lang/String", myJStringArg);
\endcode
*/
QJniObject::QJniObject(const char *className, const char *signature, const QVaListPrivate &args)
: d(new QJniObjectPrivate())
{
@ -671,6 +712,21 @@ QJniObject::QJniObject(jclass clazz, const char *signature, ...)
}
}
/*!
\fn template<typename ...Args> QJniObject::QJniObject(jclass clazz, Args &&...args)
\since 6.4
Constructs a new JNI object from \a clazz by calling the constructor with
the arguments \a args. This constructor is only available if all \a args are
known \l {JNI Types}.
\code
QJniEnvironment env;
jclass myClazz = env.findClass("org/qtproject/qt/TestClass");
QJniObject(myClazz, 3);
\endcode
*/
/*!
Constructs a new JNI object by calling the default constructor of \a clazz.
@ -883,10 +939,11 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
}
/*!
\fn template <typename T> T QJniObject::callMethod(const char *methodName, const char *signature, ...) const
\fn template <typename Ret, typename ...Args> Ret QJniObject::callMethod(const char *methodName, const char *signature, Args &&...args) const
\since 6.4
Calls the object's method \a methodName with \a signature specifying the types of any
subsequent arguments.
subsequent arguments \a args.
\code
QJniObject myJavaStrin("org/qtproject/qt/TestClass");
@ -896,21 +953,25 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
/*!
\fn template <typename T> T QJniObject::callMethod(const char *methodName) const
\fn template <typename Ret, typename ...Args> Ret QJniObject::callMethod(const char *methodName, Args &&...args) const
\since 6.4
Calls the method \a methodName and returns the value.
Calls the method \a methodName with arguments \a args and returns the value.
\code
QJniObject myJavaStrin("org/qtproject/qt/TestClass");
jint size = myJavaString.callMethod<jint>("length");
\endcode
The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
\fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, ...)
\fn template <typename Ret, typename ...Args> Ret QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
\since 6.4
Calls the static method \a methodName from class \a className with \a signature
specifying the types of any subsequent arguments.
specifying the types of any subsequent arguments \a args.
\code
jint a = 2;
@ -920,17 +981,21 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
/*!
\fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName)
\fn template <typename Ret, typename ...Args> Ret QJniObject::callStaticMethod(const char *className, const char *methodName, Args &&...args)
\since 6.4
Calls the static method \a methodName on class \a className and returns the value.
Calls the static method \a methodName on class \a className with arguments \a args,
and returns the value of type \c Ret.
\code
jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod");
\endcode
The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
\fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
\fn template <typename Ret, typename ...Args> Ret QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args)
Calls the static method \a methodName from \a clazz with \a signature
specifying the types of any subsequent arguments.
@ -945,7 +1010,8 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
/*!
\fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, ...)
\fn template <typename Ret, typename ...Args> Ret QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args)
\since 6.4
Calls the static method identified by \a methodId from the class \a clazz
with any subsequent arguments. Useful when \a clazz and \a methodId are
@ -964,7 +1030,8 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
/*!
\fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName)
\fn template <typename Ret, typename ...Args> Ret QJniObject::callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
\since 6.4
Calls the static method \a methodName on \a clazz and returns the value.
@ -973,6 +1040,8 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
jclass javaMathClass = env.findClass("java/lang/Math");
jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random");
\endcode
The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
@ -1090,32 +1159,40 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId,
}
/*!
\fn QJniObject QJniObject::callObjectMethod(const char *methodName) const
\fn template<typename Ret, typename ...Args> QJniObject QJniObject::callObjectMethod(const char *methodName, Args &&...args) const
\since 6.4
Calls the Java objects method \a methodName and returns a new QJniObject for
the returned Java object.
Calls the Java objects method \a methodName with arguments \a args and returns a
new QJniObject for the returned Java object.
\code
QJniObject myJavaString = QJniObject::fromString("Hello, Java");
QJniObject myJavaString2 = myJavaString1.callObjectMethod<jstring>("toString");
\endcode
The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
\fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName)
\fn template<typename Ret, typename ...Args> QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, Args &&...args)
\since 6.4
Calls the static method with \a methodName on the class \a className.
Calls the static method with \a methodName on the class \a className, passing
arguments \a args, and returns a new QJniObject for the returned Java object.
\code
QJniObject string = QJniObject::callStaticObjectMethod<jstring>("CustomClass", "getClassName");
\endcode
The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
\fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName)
Calls the static method with \a methodName on \a clazz.
\fn template<typename Ret, typename ...Args> QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
\since 6.4
Calls the static method with \a methodName on \a clazz, passing arguments \a args,
and returns a new QJniObject for the returned Java object.
*/
/*!

View File

@ -57,8 +57,26 @@ public:
QJniObject();
explicit QJniObject(const char *className);
explicit QJniObject(const char *className, const char *signature, ...);
template<typename ...Args
#ifndef Q_QDOC
, std::enable_if_t<!std::disjunction_v<QtJniTypes::IsStringType<std::decay_t<Args>>...>>* = nullptr
#endif
>
explicit QJniObject(const char *className, Args &&...args)
: QJniObject(className, QtJniTypes::constructorSignature<Args...>().data(),
std::forward<Args>(args)...)
{}
explicit QJniObject(jclass clazz);
explicit QJniObject(jclass clazz, const char *signature, ...);
template<typename ...Args
#ifndef Q_QDOC
, std::enable_if_t<!std::disjunction_v<QtJniTypes::IsStringType<std::decay_t<Args>>...>>* = nullptr
#endif
>
explicit QJniObject(jclass clazz, Args &&...args)
: QJniObject(clazz, QtJniTypes::constructorSignature<Args...>().data(),
std::forward<Args>(args)...)
{}
QJniObject(jobject globalRef);
~QJniObject();
@ -72,206 +90,135 @@ public:
jclass objectClass() const;
QByteArray className() const;
template <typename T>
T callMethod(const char *methodName, const char *signature, ...) const
{
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
if (id) {
va_list args;
va_start(args, signature);
callMethodForType<T>(env.jniEnv(), res, object(), id, args);
va_end(args);
if (env.checkAndClearExceptions())
res = {};
}
return res;
}
template <>
void callMethod<void>(const char *methodName, const char *signature, ...) const
template <typename Ret, typename ...Args>
Ret callMethod(const char *methodName, const char *signature, Args &&...args) const
{
QtJniTypes::assertPrimitiveType<Ret>();
QJniEnvironment env;
jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
if (id) {
va_list args;
va_start(args, signature);
callVoidMethodV(env.jniEnv(), id, args);
va_end(args);
env.checkAndClearExceptions();
if constexpr (std::is_same<Ret, void>::value) {
callVoidMethodV(env.jniEnv(), id, std::forward<Args>(args)...);
env.checkAndClearExceptions();
} else {
Ret res{};
callMethodForType<Ret>(env.jniEnv(), res, object(), id, std::forward<Args>(args)...);
if (env.checkAndClearExceptions())
res = {};
return res;
}
}
if constexpr (!std::is_same<Ret, void>::value)
return Ret{};
}
template <typename Ret, typename ...Args>
Ret callMethod(const char *methodName, Args &&...args) const
{
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
if constexpr (std::is_same<Ret, void>::value) {
callMethod<void>(methodName, signature.data(), std::forward<Args>(args)...);
} else {
QtJniTypes::assertPrimitiveType<Ret>();
return callMethod<Ret>(methodName, signature.data(), std::forward<Args>(args)...);
}
}
template <typename T>
T callMethod(const char *methodName) const
template <typename Ret, typename ...Args>
QJniObject callObjectMethod(const char *methodName, Args &&...args) const
{
QtJniTypes::assertPrimitiveType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callMethod<T>(methodName, signature);
}
template <typename T>
QJniObject callObjectMethod(const char *methodName) const
{
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callObjectMethod(methodName, signature);
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
return callObjectMethod(methodName, signature.data(), std::forward<Args>(args)...);
}
QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const;
template <typename T>
static T callStaticMethod(const char *className, const char *methodName,
const char *signature, ...)
{
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
if (clazz) {
jmethodID id = getCachedMethodID(env.jniEnv(), clazz,
QJniObject::toBinaryEncClassName(className),
methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
callStaticMethodForType<T>(env.jniEnv(), res, clazz, id, args);
va_end(args);
if (env.checkAndClearExceptions())
res = {};
}
}
return res;
}
template <>
void callStaticMethod<void>(const char *className, const char *methodName,
const char *signature, ...)
template <typename Ret, typename ...Args>
static Ret callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
{
QtJniTypes::assertPrimitiveType<Ret>();
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
if (clazz) {
jmethodID id = getCachedMethodID(env.jniEnv(), clazz,
QJniObject::toBinaryEncClassName(className),
methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
env->CallStaticVoidMethodV(clazz, id, args);
va_end(args);
env.checkAndClearExceptions();
}
}
return callStaticMethod<Ret>(clazz, methodName, signature, std::forward<Args>(args)...);
}
template <typename T>
static T callStaticMethod(const char *className, const char *methodName)
{
QtJniTypes::assertPrimitiveType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callStaticMethod<T>(className, methodName, signature);
}
template <typename T>
static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
{
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
if (clazz) {
jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
callStaticMethodForType<T>(env.jniEnv(), res, clazz, id, args);
va_end(args);
if (env.checkAndClearExceptions())
res = {};
}
}
return res;
}
template <>
void callStaticMethod<void>(jclass clazz, const char *methodName,
const char *signature, ...)
template <typename Ret, typename ...Args>
static Ret callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args)
{
QtJniTypes::assertPrimitiveType<Ret>();
QJniEnvironment env;
if (clazz) {
jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
env->CallStaticVoidMethodV(clazz, id, args);
va_end(args);
return callStaticMethod<Ret, Args...>(clazz, id, std::forward<Args>(args)...);
}
if constexpr (!std::is_same<Ret, void>::value)
return Ret{};
}
template <typename Ret, typename ...Args>
static Ret callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args)
{
QtJniTypes::assertPrimitiveType<Ret>();
QJniEnvironment env;
if (clazz && methodId) {
if constexpr (std::is_same<Ret, void>::value) {
callStaticMethodForVoid(env.jniEnv(), clazz, methodId, std::forward<Args>(args)...);
env.checkAndClearExceptions();
} else {
Ret res{};
callStaticMethodForType<Ret>(env.jniEnv(), res, clazz, methodId, std::forward<Args>(args)...);
if (env.checkAndClearExceptions())
res = {};
return res;
}
}
if constexpr (!std::is_same<Ret, void>::value)
return Ret{};
}
template <typename T>
static T callStaticMethod(jclass clazz, jmethodID methodId, ...)
template <typename Ret, typename ...Args>
static Ret callStaticMethod(const char *className, const char *methodName, Args &&...args)
{
QtJniTypes::assertPrimitiveType<T>();
QtJniTypes::assertPrimitiveType<Ret>();
QJniEnvironment env;
T res{};
if (clazz && methodId) {
va_list args;
va_start(args, methodId);
callStaticMethodForType<T>(env.jniEnv(), res, clazz, methodId, args);
va_end(args);
if (env.checkAndClearExceptions())
res = {};
}
return res;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
return callStaticMethod<Ret, Args...>(clazz, methodName, std::forward<Args>(args)...);
}
template <>
void callStaticMethod<void>(jclass clazz, jmethodID methodId, ...)
template <typename Ret, typename ...Args>
static Ret callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
{
QJniEnvironment env;
if (clazz && methodId) {
va_list args;
va_start(args, methodId);
env->CallStaticVoidMethodV(clazz, methodId, args);
va_end(args);
env.checkAndClearExceptions();
}
}
template <typename T> static T callStaticMethod(jclass clazz, const char *methodName)
{
QtJniTypes::assertPrimitiveType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callStaticMethod<T>(clazz, methodName, signature);
}
template <typename T>
static QJniObject callStaticObjectMethod(const char *className, const char *methodName)
{
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callStaticObjectMethod(className, methodName, signature);
QtJniTypes::assertPrimitiveType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
return callStaticMethod<Ret>(clazz, methodName, signature.data(), std::forward<Args>(args)...);
}
static QJniObject callStaticObjectMethod(const char *className, const char *methodName,
const char *signature, ...);
template <typename T>
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName)
{
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::methodSignature<T>();
return callStaticObjectMethod(clazz, methodName, signature);
}
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName,
const char *signature, ...);
static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...);
template <typename Ret, typename ...Args>
static QJniObject callStaticObjectMethod(const char *className, const char *methodName, Args &&...args)
{
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
return callStaticObjectMethod(className, methodName, signature.data(), std::forward<Args>(args)...);
}
template <typename Ret, typename ...Args>
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
{
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
return callStaticObjectMethod(clazz, methodName, signature.data(), std::forward<Args>(args)...);
}
template <typename T> T getField(const char *fieldName) const
{
QtJniTypes::assertPrimitiveType<T>();
@ -490,7 +437,7 @@ private:
static jmethodID getMethodID(JNIEnv *env, jclass clazz, const char *name,
const char *signature, bool isStatic = false);
void callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const;
void callVoidMethodV(JNIEnv *env, jmethodID id, ...) const;
QJniObject callObjectMethodV(const char *methodName, const char *signature,
va_list args) const;
@ -510,8 +457,11 @@ private:
template<typename T>
static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj,
jmethodID id, va_list args)
jmethodID id, ...)
{
va_list args = {};
va_start(args, id);
if constexpr(std::is_same<T, jboolean>::value)
res = env->CallBooleanMethodV(obj, id, args);
else if constexpr(std::is_same<T, jbyte>::value)
@ -530,12 +480,15 @@ private:
res = env->CallDoubleMethodV(obj, id, args);
else
QtJniTypes::staticAssertTypeMismatch();
va_end(args);
}
template<typename T>
static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz,
jmethodID id, va_list args)
jmethodID id, ...)
{
va_list args = {};
va_start(args, id);
if constexpr(std::is_same<T, jboolean>::value)
res = env->CallStaticBooleanMethodV(clazz, id, args);
else if constexpr(std::is_same<T, jbyte>::value)
@ -554,8 +507,18 @@ private:
res = env->CallStaticDoubleMethodV(clazz, id, args);
else
QtJniTypes::staticAssertTypeMismatch();
va_end(args);
}
static void callStaticMethodForVoid(JNIEnv *env, jclass clazz, jmethodID id, ...)
{
va_list args;
va_start(args, id);
env->CallStaticVoidMethodV(clazz, id, args);
va_end(args);
}
template<typename T>
static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj,
jfieldID id)

View File

@ -202,6 +202,10 @@ void tst_QJniEnvironment::findMethod()
jmethodID methodId = env.findMethod(clazz, "toString", "()Ljava/lang/String;");
QVERIFY(methodId != nullptr);
// existing method
methodId = env.findMethod<jstring>(clazz, "toString");
QVERIFY(methodId != nullptr);
// invalid signature
jmethodID invalid = env.findMethod(clazz, "unknown", "()I");
QVERIFY(invalid == nullptr);
@ -219,6 +223,10 @@ void tst_QJniEnvironment::findStaticMethod()
jmethodID staticMethodId = env.findStaticMethod(clazz, "parseInt", "(Ljava/lang/String;)I");
QVERIFY(staticMethodId != nullptr);
// existing method
staticMethodId = env.findStaticMethod<jint, jstring>(clazz, "parseInt");
QVERIFY(staticMethodId != nullptr);
QJniObject parameter = QJniObject::fromString("123");
jint result = QJniObject::callStaticMethod<jint>(clazz, staticMethodId,
parameter.object<jstring>());
@ -227,6 +235,8 @@ void tst_QJniEnvironment::findStaticMethod()
// invalid method
jmethodID invalid = env.findStaticMethod(clazz, "unknown", "()I");
QVERIFY(invalid == nullptr);
invalid = env.findStaticMethod<jint>(clazz, "unknown");
QVERIFY(invalid == nullptr);
// check that all exceptions are already cleared
QVERIFY(!env.checkAndClearExceptions());
}
@ -240,6 +250,8 @@ void tst_QJniEnvironment::findField()
// valid field
jfieldID validId = env.findField(clazz, "INT_FIELD", "I");
QVERIFY(validId != nullptr);
validId = env.findField<jint>(clazz, "INT_FIELD");
QVERIFY(validId != nullptr);
jmethodID constructorId = env.findMethod(clazz, "<init>", "()V");
QVERIFY(constructorId != nullptr);
@ -265,6 +277,8 @@ void tst_QJniEnvironment::findStaticField()
// valid field
jfieldID validId = env.findStaticField(clazz, "S_INT_FIELD", "I");
QVERIFY(validId != nullptr);
validId = env.findStaticField<jint>(clazz, "S_INT_FIELD");
QVERIFY(validId != nullptr);
int size = env->GetStaticIntField(clazz, validId);
QVERIFY(!env.checkAndClearExceptions());

View File

@ -166,6 +166,13 @@ void tst_QJniObject::ctor()
QCOMPARE(string.toString(), object.toString());
}
{
QJniObject string = QJniObject::fromString(QLatin1String("Hello, Java"));
QJniObject object("java/lang/String", string.object<jstring>());
QVERIFY(object.isValid());
QCOMPARE(string.toString(), object.toString());
}
{
QJniEnvironment env;
jclass javaStringClass = env->FindClass("java/lang/String");
@ -182,6 +189,16 @@ void tst_QJniObject::ctor()
QVERIFY(stringCpy.isValid());
QCOMPARE(qString, stringCpy.toString());
}
{
QJniEnvironment env;
const QString qString = QLatin1String("Hello, Java");
jclass javaStringClass = env->FindClass("java/lang/String");
QJniObject string = QJniObject::fromString(qString);
QJniObject stringCpy(javaStringClass, string.object<jstring>());
QVERIFY(stringCpy.isValid());
QCOMPARE(qString, stringCpy.toString());
}
}
void tst_QJniObject::callMethodTest()
@ -194,9 +211,12 @@ void tst_QJniObject::callMethodTest()
const jboolean isEmpty = jString1.callMethod<jboolean>("isEmpty");
QVERIFY(!isEmpty);
const jint ret = jString1.callMethod<jint>("compareToIgnoreCase",
"(Ljava/lang/String;)I",
jString2.object<jstring>());
jint ret = jString1.callMethod<jint>("compareToIgnoreCase",
"(Ljava/lang/String;)I",
jString2.object<jstring>());
QVERIFY(0 == ret);
ret = jString1.callMethod<jint>("compareToIgnoreCase", jString2.object<jstring>());
QVERIFY(0 == ret);
}
@ -219,6 +239,10 @@ void tst_QJniObject::callObjectMethodTest()
"(II)Ljava/lang/String;",
0, 4);
QCOMPARE(subString.toString(), qString.mid(0, 4));
subString = jString.callObjectMethod<jstring>("substring", 0, 4);
QCOMPARE(subString.toString(), qString.mid(0, 4));
}
void tst_QJniObject::stringConvertionTest()
@ -276,6 +300,16 @@ void tst_QJniObject::callStaticObjectMethodClassName()
QString returnedString = returnValue.toString();
QCOMPARE(returnedString, QString::fromLatin1("test format"));
returnValue = QJniObject::callStaticObjectMethod<jstring>("java/lang/String",
"format",
formatString.object<jstring>(),
jobjectArray(0));
QVERIFY(returnValue.isValid());
returnedString = returnValue.toString();
QCOMPARE(returnedString, QString::fromLatin1("test format"));
}
void tst_QJniObject::callStaticObjectMethod()
@ -297,6 +331,16 @@ void tst_QJniObject::callStaticObjectMethod()
QString returnedString = returnValue.toString();
QCOMPARE(returnedString, QString::fromLatin1("test format"));
returnValue = QJniObject::callStaticObjectMethod<jstring>(cls,
"format",
formatString.object<jstring>(),
jobjectArray(0));
QVERIFY(returnValue.isValid());
returnedString = returnValue.toString();
QCOMPARE(returnedString, QString::fromLatin1("test format"));
}
void tst_QJniObject::callStaticObjectMethodById()
@ -336,6 +380,9 @@ void tst_QJniObject::callStaticBooleanMethod()
"(Ljava/lang/String;)Z",
parameter.object<jstring>());
QVERIFY(b);
b = QJniObject::callStaticMethod<jboolean>(cls, "parseBoolean", parameter.object<jstring>());
QVERIFY(b);
}
{
@ -347,6 +394,9 @@ void tst_QJniObject::callStaticBooleanMethod()
"(Ljava/lang/String;)Z",
parameter.object<jstring>());
QVERIFY(!b);
b = QJniObject::callStaticMethod<jboolean>(cls, "parseBoolean", parameter.object<jstring>());
QVERIFY(!b);
}
}
@ -387,6 +437,10 @@ void tst_QJniObject::callStaticBooleanMethodClassName()
"(Ljava/lang/String;)Z",
parameter.object<jstring>());
QVERIFY(b);
b = QJniObject::callStaticMethod<jboolean>("java/lang/Boolean",
"parseBoolean",
parameter.object<jstring>());
QVERIFY(b);
}
{
@ -398,6 +452,10 @@ void tst_QJniObject::callStaticBooleanMethodClassName()
"(Ljava/lang/String;)Z",
parameter.object<jstring>());
QVERIFY(!b);
b = QJniObject::callStaticMethod<jboolean>("java/lang/Boolean",
"parseBoolean",
parameter.object<jstring>());
QVERIFY(!b);
}
}
@ -408,7 +466,6 @@ void tst_QJniObject::callStaticByteMethodClassName()
jbyte returnValue = QJniObject::callStaticMethod<jbyte>("java/lang/Byte",
"parseByte",
"(Ljava/lang/String;)B",
parameter.object<jstring>());
QCOMPARE(returnValue, jbyte(number.toInt()));
}
@ -424,7 +481,6 @@ void tst_QJniObject::callStaticByteMethod()
jbyte returnValue = QJniObject::callStaticMethod<jbyte>(cls,
"parseByte",
"(Ljava/lang/String;)B",
parameter.object<jstring>());
QCOMPARE(returnValue, jbyte(number.toInt()));
}
@ -452,7 +508,6 @@ void tst_QJniObject::callStaticIntMethodClassName()
jint returnValue = QJniObject::callStaticMethod<jint>("java/lang/Integer",
"parseInt",
"(Ljava/lang/String;)I",
parameter.object<jstring>());
QCOMPARE(returnValue, number.toInt());
}
@ -469,7 +524,6 @@ void tst_QJniObject::callStaticIntMethod()
jint returnValue = QJniObject::callStaticMethod<jint>(cls,
"parseInt",
"(Ljava/lang/String;)I",
parameter.object<jstring>());
QCOMPARE(returnValue, number.toInt());
}
@ -494,7 +548,6 @@ void tst_QJniObject::callStaticCharMethodClassName()
{
jchar returnValue = QJniObject::callStaticMethod<jchar>("java/lang/Character",
"toUpperCase",
"(C)C",
jchar('a'));
QCOMPARE(returnValue, jchar('A'));
}
@ -508,7 +561,6 @@ void tst_QJniObject::callStaticCharMethod()
jchar returnValue = QJniObject::callStaticMethod<jchar>(cls,
"toUpperCase",
"(C)C",
jchar('a'));
QCOMPARE(returnValue, jchar('A'));
}
@ -533,7 +585,6 @@ void tst_QJniObject::callStaticDoubleMethodClassName ()
jdouble returnValue = QJniObject::callStaticMethod<jdouble>("java/lang/Double",
"parseDouble",
"(Ljava/lang/String;)D",
parameter.object<jstring>());
QCOMPARE(returnValue, number.toDouble());
}
@ -550,7 +601,6 @@ void tst_QJniObject::callStaticDoubleMethod()
jdouble returnValue = QJniObject::callStaticMethod<jdouble>(cls,
"parseDouble",
"(Ljava/lang/String;)D",
parameter.object<jstring>());
QCOMPARE(returnValue, number.toDouble());
}
@ -579,7 +629,6 @@ void tst_QJniObject::callStaticFloatMethodClassName()
jfloat returnValue = QJniObject::callStaticMethod<jfloat>("java/lang/Float",
"parseFloat",
"(Ljava/lang/String;)F",
parameter.object<jstring>());
QCOMPARE(returnValue, number.toFloat());
}
@ -596,7 +645,6 @@ void tst_QJniObject::callStaticFloatMethod()
jfloat returnValue = QJniObject::callStaticMethod<jfloat>(cls,
"parseFloat",
"(Ljava/lang/String;)F",
parameter.object<jstring>());
QCOMPARE(returnValue, number.toFloat());
}
@ -624,7 +672,6 @@ void tst_QJniObject::callStaticShortMethodClassName()
jshort returnValue = QJniObject::callStaticMethod<jshort>("java/lang/Short",
"parseShort",
"(Ljava/lang/String;)S",
parameter.object<jstring>());
QCOMPARE(returnValue, number.toShort());
}
@ -641,7 +688,6 @@ void tst_QJniObject::callStaticShortMethod()
jshort returnValue = QJniObject::callStaticMethod<jshort>(cls,
"parseShort",
"(Ljava/lang/String;)S",
parameter.object<jstring>());
QCOMPARE(returnValue, number.toShort());
}
@ -669,7 +715,6 @@ void tst_QJniObject::callStaticLongMethodClassName()
jlong returnValue = QJniObject::callStaticMethod<jlong>("java/lang/Long",
"parseLong",
"(Ljava/lang/String;)J",
parameter.object<jstring>());
QCOMPARE(returnValue, jlong(number.toLong()));
}
@ -685,7 +730,6 @@ void tst_QJniObject::callStaticLongMethod()
jlong returnValue = QJniObject::callStaticMethod<jlong>(cls,
"parseLong",
"(Ljava/lang/String;)J",
parameter.object<jstring>());
QCOMPARE(returnValue, jlong(number.toLong()));
}
@ -1044,9 +1088,15 @@ void tst_QJniObject::templateApiCheck()
1,
true,
'c');
QJniObject::callStaticMethod<void>(testClassName,
"staticVoidMethodWithArgs",
1,
true,
'c');
testClass.callMethod<void>("voidMethod");
testClass.callMethod<void>("voidMethodWithArgs", "(IZC)V", 1, true, 'c');
testClass.callMethod<void>("voidMethodWithArgs", 1, true, 'c');
// jboolean -----------------------------------------------------------------------------------
QVERIFY(QJniObject::callStaticMethod<jboolean>(testClassName, "staticBooleanMethod"));
@ -1056,6 +1106,11 @@ void tst_QJniObject::templateApiCheck()
true,
true,
true));
QVERIFY(QJniObject::callStaticMethod<jboolean>(testClassName,
"staticBooleanMethodWithArgs",
true,
true,
true));
QVERIFY(testClass.callMethod<jboolean>("booleanMethod"));
QVERIFY(testClass.callMethod<jboolean>("booleanMethodWithArgs",
@ -1063,6 +1118,10 @@ void tst_QJniObject::templateApiCheck()
true,
true,
true));
QVERIFY(testClass.callMethod<jboolean>("booleanMethodWithArgs",
true,
true,
true));
// jbyte --------------------------------------------------------------------------------------
QVERIFY(QJniObject::callStaticMethod<jbyte>(testClassName,
@ -1073,9 +1132,15 @@ void tst_QJniObject::templateApiCheck()
1,
1,
1) == A_BYTE_VALUE);
QVERIFY(QJniObject::callStaticMethod<jbyte>(testClassName,
"staticByteMethodWithArgs",
jbyte(1),
jbyte(1),
jbyte(1)) == A_BYTE_VALUE);
QVERIFY(testClass.callMethod<jbyte>("byteMethod") == A_BYTE_VALUE);
QVERIFY(testClass.callMethod<jbyte>("byteMethodWithArgs", "(BBB)B", 1, 1, 1) == A_BYTE_VALUE);
QVERIFY(testClass.callMethod<jbyte>("byteMethodWithArgs", jbyte(1), jbyte(1), jbyte(1)) == A_BYTE_VALUE);
// jchar --------------------------------------------------------------------------------------
QVERIFY(QJniObject::callStaticMethod<jchar>(testClassName,
@ -1086,6 +1151,11 @@ void tst_QJniObject::templateApiCheck()
jchar(1),
jchar(1),
jchar(1)) == A_CHAR_VALUE);
QVERIFY(QJniObject::callStaticMethod<jchar>(testClassName,
"staticCharMethodWithArgs",
jchar(1),
jchar(1),
jchar(1)) == A_CHAR_VALUE);
QVERIFY(testClass.callMethod<jchar>("charMethod") == A_CHAR_VALUE);
QVERIFY(testClass.callMethod<jchar>("charMethodWithArgs",
@ -1093,6 +1163,10 @@ void tst_QJniObject::templateApiCheck()
jchar(1),
jchar(1),
jchar(1)) == A_CHAR_VALUE);
QVERIFY(testClass.callMethod<jchar>("charMethodWithArgs",
jchar(1),
jchar(1),
jchar(1)) == A_CHAR_VALUE);
// jshort -------------------------------------------------------------------------------------
QVERIFY(QJniObject::callStaticMethod<jshort>(testClassName,
@ -1103,6 +1177,11 @@ void tst_QJniObject::templateApiCheck()
jshort(1),
jshort(1),
jshort(1)) == A_SHORT_VALUE);
QVERIFY(QJniObject::callStaticMethod<jshort>(testClassName,
"staticShortMethodWithArgs",
jshort(1),
jshort(1),
jshort(1)) == A_SHORT_VALUE);
QVERIFY(testClass.callMethod<jshort>("shortMethod") == A_SHORT_VALUE);
QVERIFY(testClass.callMethod<jshort>("shortMethodWithArgs",
@ -1110,6 +1189,10 @@ void tst_QJniObject::templateApiCheck()
jshort(1),
jshort(1),
jshort(1)) == A_SHORT_VALUE);
QVERIFY(testClass.callMethod<jshort>("shortMethodWithArgs",
jshort(1),
jshort(1),
jshort(1)) == A_SHORT_VALUE);
// jint ---------------------------------------------------------------------------------------
QVERIFY(QJniObject::callStaticMethod<jint>(testClassName,
@ -1120,6 +1203,11 @@ void tst_QJniObject::templateApiCheck()
jint(1),
jint(1),
jint(1)) == A_INT_VALUE);
QVERIFY(QJniObject::callStaticMethod<jint>(testClassName,
"staticIntMethodWithArgs",
jint(1),
jint(1),
jint(1)) == A_INT_VALUE);
QVERIFY(testClass.callMethod<jint>("intMethod") == A_INT_VALUE);
QVERIFY(testClass.callMethod<jint>("intMethodWithArgs",
@ -1127,6 +1215,10 @@ void tst_QJniObject::templateApiCheck()
jint(1),
jint(1),
jint(1)) == A_INT_VALUE);
QVERIFY(testClass.callMethod<jint>("intMethodWithArgs",
jint(1),
jint(1),
jint(1)) == A_INT_VALUE);
// jlong --------------------------------------------------------------------------------------
QVERIFY(QJniObject::callStaticMethod<jlong>(testClassName,
@ -1137,6 +1229,11 @@ void tst_QJniObject::templateApiCheck()
jlong(1),
jlong(1),
jlong(1)) == A_LONG_VALUE);
QVERIFY(QJniObject::callStaticMethod<jlong>(testClassName,
"staticLongMethodWithArgs",
jlong(1),
jlong(1),
jlong(1)) == A_LONG_VALUE);
QVERIFY(testClass.callMethod<jlong>("longMethod") == A_LONG_VALUE);
QVERIFY(testClass.callMethod<jlong>("longMethodWithArgs",
@ -1144,6 +1241,10 @@ void tst_QJniObject::templateApiCheck()
jlong(1),
jlong(1),
jlong(1)) == A_LONG_VALUE);
QVERIFY(testClass.callMethod<jlong>("longMethodWithArgs",
jlong(1),
jlong(1),
jlong(1)) == A_LONG_VALUE);
// jfloat -------------------------------------------------------------------------------------
QVERIFY(QJniObject::callStaticMethod<jfloat>(testClassName,
@ -1154,6 +1255,11 @@ void tst_QJniObject::templateApiCheck()
jfloat(1.1),
jfloat(1.1),
jfloat(1.1)) == A_FLOAT_VALUE);
QVERIFY(QJniObject::callStaticMethod<jfloat>(testClassName,
"staticFloatMethodWithArgs",
jfloat(1.1),
jfloat(1.1),
jfloat(1.1)) == A_FLOAT_VALUE);
QVERIFY(testClass.callMethod<jfloat>("floatMethod") == A_FLOAT_VALUE);
QVERIFY(testClass.callMethod<jfloat>("floatMethodWithArgs",
@ -1161,6 +1267,10 @@ void tst_QJniObject::templateApiCheck()
jfloat(1.1),
jfloat(1.1),
jfloat(1.1)) == A_FLOAT_VALUE);
QVERIFY(testClass.callMethod<jfloat>("floatMethodWithArgs",
jfloat(1.1),
jfloat(1.1),
jfloat(1.1)) == A_FLOAT_VALUE);
// jdouble ------------------------------------------------------------------------------------
QVERIFY(QJniObject::callStaticMethod<jdouble>(testClassName,
@ -1171,6 +1281,11 @@ void tst_QJniObject::templateApiCheck()
jdouble(1.1),
jdouble(1.1),
jdouble(1.1)) == A_DOUBLE_VALUE);
QVERIFY(QJniObject::callStaticMethod<jdouble>(testClassName,
"staticDoubleMethodWithArgs",
jdouble(1.1),
jdouble(1.1),
jdouble(1.1)) == A_DOUBLE_VALUE);
QVERIFY(testClass.callMethod<jdouble>("doubleMethod") == A_DOUBLE_VALUE);
QVERIFY(testClass.callMethod<jdouble>("doubleMethodWithArgs",
@ -1178,6 +1293,10 @@ void tst_QJniObject::templateApiCheck()
jdouble(1.1),
jdouble(1.1),
jdouble(1.1)) == A_DOUBLE_VALUE);
QVERIFY(testClass.callMethod<jdouble>("doubleMethodWithArgs",
jdouble(1.1),
jdouble(1.1),
jdouble(1.1)) == A_DOUBLE_VALUE);
// jobject ------------------------------------------------------------------------------------
{