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 QtJniMethods {
namespace Detail { namespace Detail {
// Various helpers to forward the call to the registered function (with JNI types // A helper to forward the call to the registered function (with JNI types
// as arguments) to the real function with proper type conversion. This is needed // as arguments) to the real function, using the type conversion implemented in
// because we want to write functions that take QJniObjects (subclasses), while // QJniObject::LocalFrame::convertTo/FromJni. This is needed because we want to
// Java can only call functions that take jobjects. // write functions that take Qt-style arguments (QJniObject, declared types,
// QList etc), while Java can only call functions that take jobjects.
// Map any QJniObject type to jobject
template <typename Arg> template <typename Arg>
struct JNITypeForArgImpl struct JNITypeForArgImpl
{ {
using Type = std::conditional_t<std::disjunction_v<std::is_base_of<QJniObject, Arg>, using LocalFrame = QtJniTypes::Detail::LocalFrame<void>;
std::is_base_of<QtJniTypes::JObjectBase, Arg>>, using JNIType = decltype(std::declval<LocalFrame>().convertToJni(std::declval<Arg>()));
jobject, Arg>; static Arg fromVarArg(JNIType t) // JNIType is always POD
static Arg fromVarArg(Type t)
{ {
return static_cast<Arg>(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 <>
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>{};
} }
}; };
template <typename Arg> template <typename Arg>
using JNITypeForArg = typename JNITypeForArgImpl<std::decay_t<Arg>>::Type; using JNITypeForArg = typename JNITypeForArgImpl<std::decay_t<Arg>>::JNIType;
} // namespace Detail } // namespace Detail
} // namespace QtJniMethods } // namespace QtJniMethods