Commit 429987f0 authored by Volker Krause's avatar Volker Krause
Browse files

Add static method JNI wrappers

This is largely reusing the infrastructure for non-static methods and is
otherwise also pretty similar.
parent 6594b7c6
Pipeline #104854 failed with stage
in 2 minutes and 55 seconds
......@@ -29,6 +29,11 @@ public:
JNI_PROPERTY(java::lang::String, name)
inline QAndroidJniObject handle() const { return m_handle; }
JNI_STATIC_METHOD(void, noRetNoArg)
JNI_STATIC_METHOD(jlong, retNoArg)
JNI_STATIC_METHOD(void, noRetArg, java::lang::String)
JNI_STATIC_METHOD(android::content::Intent, retArg, bool)
private:
QAndroidJniObject m_handle;
};
......@@ -130,6 +135,24 @@ private Q_SLOTS:
s = obj.getStringList();
obj.setFlags(nullptr);
#endif
#endif
}
void testStaticCalls()
{
#ifndef Q_OS_ANDROID
QAndroidJniObject::m_staticProtocol.clear();
TestClass::noRetNoArg();
TestClass::noRetArg(QStringLiteral("test"));
TestClass::retNoArg();
QAndroidJniObject o = TestClass::retArg(true);
QCOMPARE(QAndroidJniObject::m_staticProtocol.size(), 3);
QCOMPARE(QAndroidJniObject::m_staticProtocol[0], QLatin1String("callStaticMethod: android/content/Intent noRetNoArg ()V ()"));
QCOMPARE(QAndroidJniObject::m_staticProtocol[1], QLatin1String("callStaticMethod: android/content/Intent noRetArg (Ljava/lang/String;)V (o)"));
QCOMPARE(QAndroidJniObject::m_staticProtocol[2], QLatin1String("callStaticMethod: android/content/Intent retNoArg ()J ()"));
QCOMPARE(o.protocol().at(0), QLatin1String("android/content/Intent retArg (Z)Landroid/content/Intent; (Z)"));
#endif
}
};
......
......@@ -119,10 +119,44 @@ public:
addToProtocol(QLatin1String("setField: ") + QLatin1String(fieldName) + QLatin1Char(' ') + QLatin1String(signature));
}
static QAndroidJniObject callStaticObjectMethod(const char *methodName, const char *signature, ...)
template <typename T, typename ...Args>
static T callStaticMethod(const char *className, const char *methodName, const char *signature, Args...)
{
const QString s = QLatin1String("callStaticMethod: ") + QLatin1String(className) + QLatin1Char(' ')
+ QLatin1String(methodName) + QLatin1Char(' ') + QLatin1String(signature) + QLatin1String(" (")
+ (...+QLatin1String(KAndroidExtras::Internal::argTypeToString<Args>())) + QLatin1Char(')');
m_staticProtocol.push_back(s);
if constexpr (!std::is_same_v<T, void>) {
return {};
}
}
template <typename T>
static T callStaticMethod(const char *className, const char *methodName, const char *signature)
{
const QString s = QLatin1String("callStaticMethod: ") + QLatin1String(className) + QLatin1Char(' ')
+ QLatin1String(methodName) + QLatin1Char(' ') + QLatin1String(signature) + QLatin1String(" ()");
m_staticProtocol.push_back(s);
if constexpr (!std::is_same_v<T, void>) {
return {};
}
}
template <typename ...Args>
static QAndroidJniObject callStaticObjectMethod(const char *className, const char *methodName, const char *signature, Args...)
{
const QString s = QLatin1String("callStaticObjectMethod: ") + QLatin1String(className) + QLatin1Char(' ')
+ QLatin1String(methodName) + QLatin1Char(' ') + QLatin1String(signature) + QLatin1String(" (")
+ (...+QLatin1String(KAndroidExtras::Internal::argTypeToString<Args>())) + QLatin1Char(')');
QAndroidJniObject obj;
obj.addToProtocol(s);
return obj;
}
static QAndroidJniObject callStaticObjectMethod(const char *className, const char *methodName, const char *signature)
{
QAndroidJniObject obj;
obj.addToProtocol(QLatin1String("callStaticObjectMethod: ") + QLatin1String(methodName) + QLatin1Char(' ') + QLatin1String(signature));
obj.addToProtocol(QLatin1String("callStaticObjectMethod: ") + QLatin1String(className) + QLatin1Char(' ')
+ QLatin1String(methodName) + QLatin1Char(' ') + QLatin1String(signature) + QLatin1String(" ()"));
return obj;
}
......
......@@ -168,6 +168,40 @@ namespace Internal {
}
}
};
// static call wrapper
template <typename RetT, typename ...Sig>
struct static_invoker {
template <typename ...Args>
static typename Internal::call_return<RetT>::CallReturnT call(const char *className, const char *name, const char *signature, Args&&... args)
{
static_assert(is_call_compatible<Sig...>::template with<Args...>::value, "incompatible call arguments");
const auto params = std::make_tuple(toCallArgument<Sig, Args>(std::forward<Args>(args))...);
return doCall(className, name, signature, params, std::index_sequence_for<Args...>{});
}
template <typename ParamT, std::size_t ...Index>
static typename Internal::call_return<RetT>::CallReturnT doCall(const char *className, const char *name, const char *signature, const ParamT &params, std::index_sequence<Index...>)
{
if constexpr (Jni::is_basic_type<RetT>::value) {
return QAndroidJniObject::callStaticMethod<RetT>(className, name, signature, toFinalCallArgument(std::get<Index>(params))...);
} else {
return Internal::call_return<RetT>::toReturnValue(QAndroidJniObject::callStaticObjectMethod(className, name, signature, toFinalCallArgument(std::get<Index>(params))...));
}
}
};
template <typename RetT>
struct static_invoker<RetT> {
static typename Internal::call_return<RetT>::CallReturnT call(const char *className, const char *name, const char *signature)
{
if constexpr (Jni::is_basic_type<RetT>::value) {
return QAndroidJniObject::callStaticMethod<RetT>(className, name, signature);
} else {
return Internal::call_return<RetT>::toReturnValue(QAndroidJniObject::callStaticObjectMethod(className, name, signature));
}
}
};
}
///@endcond
......@@ -204,6 +238,38 @@ inline KAndroidExtras::Internal::call_return<RetT>::CallReturnT Name(Args&&... a
return Internal::invoker<RetT, ## __VA_ARGS__>::call(handle(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>(), std::forward<Args>(args)...); \
}
/**
* Wrap a JNI static method call.
* This will add a static method named @p name to the current class. Argument types are checked at compile time,
* with the following inputs being accepted:
* - basic types have to match exactly
* - non-basic types can be either passed as @c QAndroidJniObject instance or with a type that has an
* conversion registered with @c JNI_DECLARE_CONVERTER.
*
* The return type of the method is determined as follows:
* - basic types are returned directly
* - non-basic types without a registered type conversion are returned as @c QAndroidJniObject.
* - non-basic types with a registered type conversion are returned in a wrapper class that can
* be implicitly converted either to the destination type of the conversion, or a @c QAndroidJniObject.
* This allows to avoid type conversion when chaining calls for example, it however needs additional
* care when used in combination with automatic type deduction.
* - array return types also result in a wrapper class that can be implicitly converted to a sequential
* container or a @p QAndroidJniObject representing the JNI array.
*
* Thie macro can only be placed in classes having @c JNI_OBJECT macro.
*
* @param RetT The return type. Must either be a basic type or a type declared with @c JNI_TYPE
* @param Name The name of the method. Must match the JNI method to be called exactly.
* @param Args A list or argument types (can be empty). Must either be basic types or types declared
* with @c JNI_TYPE.
*/
#define JNI_STATIC_METHOD(RetT, Name, ...) \
template <typename ...Args> \
static inline KAndroidExtras::Internal::call_return<RetT>::CallReturnT Name(Args&&... args) { \
using namespace KAndroidExtras; \
return Internal::static_invoker<RetT, ## __VA_ARGS__>::call(Jni::typeName<_jni_ThisType>(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>(), std::forward<Args>(args)...); \
}
}
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment