JNI: Simplify type conversion for native functions, reuse existing code

The QJniObject::LocalFrame has all the logic for converting between Qt
types and corresponding JNI types. Instead of implementing the same
type mapping for native function calls, reuse LocalFrame, with the
special handling of cases where no conversion is necessary at all.

Change-Id: I4a94b22788669387330ec1b3e27bb06eb6d64b7b
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Volker Krause <vkrause@kde.org>
This commit is contained in:
Volker Hilsheimer 2024-12-26 19:18:57 +01:00
parent 0d0b346322
commit 9f10fd8374

View File

@ -127,64 +127,32 @@ QT_OVERLOADED_MACRO(Q_DECLARE_JNI_CLASS_SPECIALIZATION, __VA_ARGS__)
namespace QtJniMethods {
namespace Detail {
// Various helpers to forward the call to the registered function (with JNI types
// as arguments) to the real function with proper type conversion. This is needed
// because we want to write functions that take QJniObjects (subclasses), while
// Java can only call functions that take jobjects.
// Map any QJniObject type to jobject
// A helper to forward the call to the registered function (with JNI types
// as arguments) to the real function, using the type conversion implemented in
// QJniObject::LocalFrame::convertTo/FromJni. This is needed because we want to
// write functions that take Qt-style arguments (QJniObject, declared types,
// QList etc), while Java can only call functions that take jobjects.
template <typename Arg>
struct JNITypeForArgImpl
{
using Type = std::conditional_t<std::disjunction_v<std::is_base_of<QJniObject, Arg>,
std::is_base_of<QtJniTypes::JObjectBase, Arg>>,
jobject, Arg>;
static Arg fromVarArg(Type t)
using LocalFrame = QtJniTypes::Detail::LocalFrame<void>;
using JNIType = decltype(std::declval<LocalFrame>().convertToJni(std::declval<Arg>()));
static Arg fromVarArg(JNIType t) // JNIType is always POD
{
return static_cast<Arg>(t);
}
};
template <>
struct JNITypeForArgImpl<QString>
{
using Type = jstring;
static QString fromVarArg(Type t)
{
return t ? QtJniTypes::Detail::toQString(t, QJniEnvironment::getJniEnv()) : QString();
}
};
template <typename T>
struct JNITypeForArgImpl<QJniArray<T>>
{
using Type = jobject;
static QJniArray<T> fromVarArg(Type t)
{
return QJniArray<T>(t);
}
};
template <typename T>
struct JNITypeForArgImpl<QList<T>>
{
private:
using ArrayType = decltype(QJniArrayBase::fromContainer(std::declval<QList<T>>()));
using ArrayObjectType = decltype(std::declval<ArrayType>().arrayObject());
using ElementType = typename ArrayType::value_type;
public:
using Type = ArrayObjectType;
static QList<T> fromVarArg(Type t)
{
return t ? QJniArray<ElementType>(t).toContainer() : QList<T>{};
// Special case: if convertToJni doesn't do anything, don't do anything
// here either. convertFromJni would always give us a QJniObject (so
// that QJniObject::callMethod etc returns an owning QJniObject).
if constexpr (std::is_same_v<JNIType, Arg>) {
return t;
} else {
LocalFrame frame;
return frame.template convertFromJni<Arg>(t);
}
}
};
template <typename Arg>
using JNITypeForArg = typename JNITypeForArgImpl<std::decay_t<Arg>>::Type;
using JNITypeForArg = typename JNITypeForArgImpl<std::decay_t<Arg>>::JNIType;
} // namespace Detail
} // namespace QtJniMethods