Commit eff513f8 authored by Volker Krause's avatar Volker Krause
Browse files

Support implicit argument type conversions for method wrappers

This is needed in particular to pass property wrappers directly to calls.
parent 015f5365
......@@ -22,6 +22,10 @@ public:
JNI_METHOD(void, setFlags, jint)
JNI_METHOD(void, start)
JNI_METHOD(bool, setCoordinates, jfloat, jfloat)
JNI_METHOD(void, startIntent, android::content::Intent)
JNI_METHOD(android::content::Intent, getIntent)
JNI_PROPERTY(java::lang::String, name)
inline QAndroidJniObject handle() const { return m_handle; }
private:
......@@ -38,8 +42,8 @@ private Q_SLOTS:
static_assert(Internal::is_argument_compatible<java::lang::String, QAndroidJniObject>::value);
static_assert(Internal::is_argument_compatible<java::lang::String, QString>::value);
static_assert(!Internal::is_argument_compatible<jint, jdouble>::value);
static_assert(!Internal::is_argument_compatible<jint, QAndroidJniObject>::value);
static_assert(!Internal::is_argument_compatible<jobject, jint>::value);
static_assert(!Internal::is_argument_compatible<java::lang::String, QUrl>::value);
static_assert(Internal::is_call_compatible<jint>::with<jint>::value);
......@@ -47,6 +51,10 @@ private Q_SLOTS:
static_assert(!Internal::is_call_compatible<java::lang::String, jfloat>::with<float, QAndroidJniObject>::value);
static_assert(Internal::is_call_compatible<java::lang::String, java::lang::String>::with<QString, QAndroidJniObject>::value);
static_assert(Internal::is_call_compatible<java::lang::String, java::lang::String>::with<QAndroidJniObject, QString>::value);
// implicit conversion
static_assert(Internal::is_argument_compatible<jint, jdouble>::value);
static_assert(Internal::is_argument_compatible<jfloat, jdouble>::value);
}
void testMethodCalls()
......@@ -64,7 +72,21 @@ private Q_SLOTS:
bool b = obj.setCoordinates(0.0f, 0.0f);
Q_UNUSED(b)
QCOMPARE(obj.handle().protocol().size(), 7);
// implicit conversion
b = obj.setCoordinates(0.0, 0.0);
// implicit conversion, and must not copy the property wrapper
obj.setName(obj.name);
// implicit conversion from manual wrappers
Intent intent;
obj.startIntent(intent);
// returning a non-wrapped type
QAndroidJniObject j = obj.getIntent();
// lvalue QAndroidJniObject argument
obj.setName(j);
// implicit conversion from a static property wrapper
obj.setFlags(Intent::FLAG_GRANT_READ_URI_PERMISSION);
QCOMPARE(obj.handle().protocol().size(), 14);
QCOMPARE(obj.handle().protocol()[0], QLatin1String("callObjectMethod: getName ()Ljava/lang/String;"));
QCOMPARE(obj.handle().protocol()[1], QLatin1String("callMethod: setName (Ljava/lang/String;)V"));
QCOMPARE(obj.handle().protocol()[2], QLatin1String("callMethod: setName (Ljava/lang/String;)V"));
......@@ -73,6 +95,14 @@ private Q_SLOTS:
QCOMPARE(obj.handle().protocol()[5], QLatin1String("callMethod: start ()V"));
QCOMPARE(obj.handle().protocol()[6], QLatin1String("callMethod: setCoordinates (FF)Z"));
QCOMPARE(obj.handle().protocol()[7], QLatin1String("callMethod: setCoordinates (FF)Z"));
QCOMPARE(obj.handle().protocol()[8], QLatin1String("getObjectField: name Ljava/lang/String;"));
QCOMPARE(obj.handle().protocol()[9], QLatin1String("callMethod: setName (Ljava/lang/String;)V"));
QCOMPARE(obj.handle().protocol()[10], QLatin1String("callMethod: startIntent (Landroid/content/Intent;)V"));
QCOMPARE(obj.handle().protocol()[11], QLatin1String("callObjectMethod: getIntent ()Landroid/content/Intent;"));
QCOMPARE(obj.handle().protocol()[12], QLatin1String("callMethod: setName (Ljava/lang/String;)V"));
QCOMPARE(obj.handle().protocol()[13], QLatin1String("callMethod: setFlags (I)V"));
#if 0
// stuff that must not compile
obj.setName(42);
......
......@@ -18,10 +18,11 @@ namespace Internal {
template <typename SigT, typename ArgT> struct is_argument_compatible
{
static inline constexpr bool value =
!std::is_same<SigT, void>::value && !std::is_same<ArgT, void>::value &&
(std::is_same<SigT, ArgT>::value ||
(!Jni::is_basic_type<SigT>::value && std::is_same<ArgT, QAndroidJniObject>::value) ||
(!Jni::is_basic_type<SigT>::value && std::is_same<ArgT, typename Jni::converter<SigT>::type>::value));
!std::is_same_v<SigT, void> && !std::is_same_v<ArgT, void> &&
(std::is_convertible_v<ArgT, SigT> ||
(!Jni::is_basic_type<SigT>::value && std::is_convertible_v<ArgT, typename Jni::converter<SigT>::type>) ||
(!Jni::is_basic_type<SigT>::value && std::is_convertible_v<ArgT, QAndroidJniObject>) ||
(!Jni::is_basic_type<SigT>::value && std::is_convertible_v<ArgT, typename Jni::converter<SigT>::type>));
};
template <typename ...Sig> struct is_call_compatible
......@@ -34,22 +35,22 @@ namespace Internal {
};
// argument type conversion
template <typename ArgT, bool is_basic, bool is_convertible> struct call_argument {};
template <typename ArgT> struct call_argument<ArgT, true, false>
template <typename SigT, typename ArgT, bool is_basic, bool is_convertible> struct call_argument {};
template <typename SigT, typename ArgT> struct call_argument<SigT, ArgT, true, false>
{
inline constexpr ArgT operator()(ArgT value) const
inline constexpr SigT operator()(ArgT value) const
{
return value;
}
};
template <typename ArgT> struct call_argument<ArgT, false, true>
template <typename SigT, typename ArgT> struct call_argument<SigT, ArgT, false, true>
{
inline constexpr jobject operator()(const ArgT &value) const
inline constexpr jobject operator()(const typename Jni::converter<SigT>::type &value) const
{
return Jni::converter<ArgT>::convert(value).object();
return Jni::reverse_converter<SigT>::type::convert(value).object();
}
};
template <> struct call_argument<QAndroidJniObject, false, false>
template <typename ArgT, typename SigT> struct call_argument<SigT, ArgT, false, false>
{
inline jobject operator()(const QAndroidJniObject &value) const
{
......@@ -57,7 +58,7 @@ namespace Internal {
}
};
template <typename ArgT> constexpr call_argument<ArgT, Jni::is_basic_type<ArgT>::value, !std::is_same_v<typename Jni::converter<ArgT>::type, void>> toCallArgument = {};
template <typename SigT, typename ArgT> constexpr call_argument<SigT, std::remove_reference_t<ArgT>, Jni::is_basic_type<SigT>::value, (!std::is_same_v<typename Jni::converter<SigT>::type, void> && !std::is_same_v<std::remove_cv_t<std::remove_reference_t<ArgT>>, QAndroidJniObject>)> toCallArgument = {};
// return type conversion
template <typename RetT>
......@@ -65,8 +66,8 @@ namespace Internal {
static inline constexpr bool is_basic = Jni::is_basic_type<RetT>::value;
static inline constexpr bool is_convertible = !std::is_same_v<typename Jni::converter<RetT>::type, void>;
typedef std::conditional_t<is_basic || !is_convertible, RetT, typename Jni::converter<RetT>::type> CallReturnT;
typedef std::conditional_t<is_basic, RetT, QAndroidJniObject> JniReturnT;
typedef std::conditional_t<is_basic || !is_convertible, JniReturnT, typename Jni::converter<RetT>::type> CallReturnT;
static inline constexpr CallReturnT toReturnValue(JniReturnT value)
{
......@@ -86,13 +87,13 @@ namespace Internal {
template <typename RetT, typename ...Sig>
struct invoker {
template <typename ...Args>
static typename Internal::call_return<RetT>::CallReturnT call(QAndroidJniObject handle, const char *name, const char *signature, Args... args)
static typename Internal::call_return<RetT>::CallReturnT call(QAndroidJniObject handle, const char *name, const char *signature, Args&&... args)
{
static_assert(is_call_compatible<Sig...>::template with<Args...>::value, "incompatible call arguments");
if constexpr (Jni::is_basic_type<RetT>::value) {
return handle.callMethod<RetT>(name, signature, toCallArgument<Args>(args)...);
return handle.callMethod<RetT>(name, signature, toCallArgument<Sig, Args>(args)...);
} else {
return Internal::call_return<RetT>::toReturnValue(handle.callObjectMethod(name, signature, toCallArgument<Args>(args)...));
return Internal::call_return<RetT>::toReturnValue(handle.callObjectMethod(name, signature, toCallArgument<Sig, Args>(std::forward<Args>(args))...));
}
return {};
}
......@@ -101,10 +102,10 @@ namespace Internal {
template <typename ...Sig>
struct invoker<void, Sig...> {
template <typename ...Args>
static void call(QAndroidJniObject handle, const char *name, const char *signature, Args... args)
static void call(QAndroidJniObject handle, const char *name, const char *signature, Args&&... args)
{
static_assert(is_call_compatible<Sig...>::template with<Args...>::value, "incompatible call arguments");
handle.callMethod<void>(name, signature, toCallArgument<Args>(std::forward<Args>(args))...);
handle.callMethod<void>(name, signature, toCallArgument<Sig, Args>(std::forward<Args>(args))...);
}
};
......@@ -148,7 +149,7 @@ namespace Internal {
*/
#define JNI_METHOD(RetT, Name, ...) \
template <typename ...Args> \
inline Internal::call_return<RetT>::CallReturnT Name(Args... args) const { \
inline Internal::call_return<RetT>::CallReturnT Name(Args&&... args) const { \
return Internal::invoker<RetT, ## __VA_ARGS__>::call(handle(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>(), std::forward<Args>(args)...); \
}
......
Supports Markdown
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