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

Tidy up JNI primitive type handling

This now prevents the use of basic C++ types that don't map to JNI types
(e.g. unsigned integers, char) and it takes care of properly converting
bool to/from the JNI jboolean, which is a special case as the underlying
types don't match.
parent 4afc5f07
......@@ -60,7 +60,7 @@ private Q_SLOTS:
JNIEnv::m_arrayLength = 3;
QAndroidJniObject array;
// basic types
// primitive types
const auto a1 = Jni::Array<jint>(array);
QCOMPARE(a1.size(), 3);
QCOMPARE(std::distance(a1.begin(), a1.end()), 3);
......
......@@ -32,7 +32,7 @@ public:
// overloads
JNI_METHOD(void, overloaded)
JNI_METHOD(void, overloaded, jint)
JNI_METHOD(void, overloaded, java::lang::String, jboolean)
JNI_METHOD(void, overloaded, java::lang::String, bool)
JNI_METHOD(jint, overloaded, jlong, java::lang::String, Jni::Array<jshort>)
JNI_METHOD(Jni::Array<jshort>, overloaded, Intent)
......@@ -43,6 +43,17 @@ public:
JNI_STATIC_METHOD(void, noRetArg, java::lang::String)
JNI_STATIC_METHOD(android::content::Intent, retArg, bool)
// bool/jboolean arguments
JNI_METHOD(void, setBool, bool)
JNI_CONSTRUCTOR(TestClass, bool)
// basic C++ types that do not map to JNI (must not compile)
// JNI_METHOD(void, setUnsigned, uint32_t)
// JNI_STATIC_METHOD(void, setStaticUnsigned, uint64_t)
// JNI_METHOD(char, charReturn)
// JNI_STATIC_METHOD(uint64_t, staticCharReturn);
// JNI_CONSTRUCTOR(TestClass, char)
friend class JniMethodTest;
};
......@@ -101,8 +112,10 @@ private Q_SLOTS:
Jni::Array<jshort> l4 = obj.overloaded(intent);
obj.overloaded(23, QStringLiteral("world"), l4);
// bool conversion
obj.setBool(true);
QCOMPARE(obj.jniHandle().protocol().size(), 27);
QCOMPARE(obj.jniHandle().protocol().size(), 28);
QCOMPARE(obj.jniHandle().protocol()[0], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.jniHandle().protocol()[1], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.jniHandle().protocol()[2], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
......@@ -132,11 +145,15 @@ private Q_SLOTS:
QCOMPARE(obj.jniHandle().protocol()[24], QLatin1String("callMethod: overloaded (Ljava/lang/String;Z)V (oZ)"));
QCOMPARE(obj.jniHandle().protocol()[25], QLatin1String("callObjectMethod: overloaded (Landroid/content/Intent;)[S (o)"));
QCOMPARE(obj.jniHandle().protocol()[26], QLatin1String("callMethod: overloaded (JLjava/lang/String;[S)I (Joo)"));
QCOMPARE(obj.jniHandle().protocol()[27], QLatin1String("callMethod: setBool (Z)V (Z)"));
// ctor call
obj = TestClass(intent);
QCOMPARE(obj.jniHandle().protocol().size(), 1);
QCOMPARE(obj.jniHandle().protocol()[0], QLatin1String("ctor: android/content/Intent (Landroid/content/Intent;)V (o)"));
obj = TestClass(false);
QCOMPARE(obj.jniHandle().protocol().size(), 1);
QCOMPARE(obj.jniHandle().protocol()[0], QLatin1String("ctor: android/content/Intent (Z)V (Z)"));
#if 0
// stuff that must not compile
obj.setName(42);
......
......@@ -22,11 +22,16 @@ class TestClass
JNI_CONSTANT(java::lang::String, ACTION_CREATE_DOCUMENT)
JNI_CONSTANT(jint, FLAG_GRANT_READ_URI_PERMISSION)
JNI_CONSTANT(android::net::Uri, OBJECT_TYPE_PROPERTY)
JNI_CONSTANT(bool, BOOL_PROPERTY)
// JNI_CONSTANT(uint32_t, UNSIGNED_PROPERTY) // must not compile
JNI_PROPERTY(java::lang::String, myStringField)
JNI_PROPERTY(int, myIntField)
JNI_PROPERTY(android::net::Uri, myUriField)
JNI_PROPERTY(android::content::Intent, myIntentField)
JNI_PROPERTY(bool, myBoolProperty)
// JNI_PROPERTY(uint32_t, myUsignedProperty) // must not compile
public:
friend class JniPropertyTest;
};
......@@ -46,15 +51,18 @@ private Q_SLOTS:
Q_UNUSED(p2)
QAndroidJniObject p3 = TestClass::OBJECT_TYPE_PROPERTY;
Q_UNUSED(p3)
bool b = TestClass::BOOL_PROPERTY;
QCOMPARE(QAndroidJniObject::m_staticProtocol.size(), 3);
QCOMPARE(QAndroidJniObject::m_staticProtocol.size(), 4);
QCOMPARE(QAndroidJniObject::m_staticProtocol.at(0), QLatin1String("getStaticObjectField: android/content/Intent ACTION_CREATE_DOCUMENT Ljava/lang/String;"));
QCOMPARE(QAndroidJniObject::m_staticProtocol.at(1), QLatin1String("getStaticField<>: android/content/Intent FLAG_GRANT_READ_URI_PERMISSION I"));
QCOMPARE(QAndroidJniObject::m_staticProtocol.at(2), QLatin1String("getStaticObjectField: android/content/Intent OBJECT_TYPE_PROPERTY Landroid/net/Uri;"));
QCOMPARE(QAndroidJniObject::m_staticProtocol.at(3), QLatin1String("getStaticField<>: android/content/Intent BOOL_PROPERTY Z"));
const QString p4 = ManifestPermission::READ_CALENDAR;
Q_UNUSED(p4)
QCOMPARE(QAndroidJniObject::m_staticProtocol.at(3), QLatin1String("getStaticObjectField: android/Manifest$permission READ_CALENDAR Ljava/lang/String;"));
QCOMPARE(QAndroidJniObject::m_staticProtocol.at(4), QLatin1String("getStaticObjectField: android/Manifest$permission READ_CALENDAR Ljava/lang/String;"));
TestClass obj;
const QString foo = obj.myStringField;
......@@ -66,8 +74,10 @@ private Q_SLOTS:
obj.myUriField = url;
const QAndroidJniObject bla = obj.myIntentField;
obj.myIntentField = bla;
b = obj.myBoolProperty;
obj.myBoolProperty = b;
QCOMPARE(obj.jniHandle().protocol().size(), 8);
QCOMPARE(obj.jniHandle().protocol().size(), 10);
QCOMPARE(obj.jniHandle().protocol().at(0), QLatin1String("getObjectField: myStringField Ljava/lang/String;"));
QCOMPARE(obj.jniHandle().protocol().at(1), QLatin1String("setField: myStringField Ljava/lang/String;"));
QCOMPARE(obj.jniHandle().protocol().at(2), QLatin1String("getField: myIntField I"));
......@@ -76,6 +86,8 @@ private Q_SLOTS:
QCOMPARE(obj.jniHandle().protocol().at(5), QLatin1String("setField: myUriField Landroid/net/Uri;"));
QCOMPARE(obj.jniHandle().protocol().at(6), QLatin1String("getObjectField: myIntentField Landroid/content/Intent;"));
QCOMPARE(obj.jniHandle().protocol().at(7), QLatin1String("setField: myIntentField Landroid/content/Intent;"));
QCOMPARE(obj.jniHandle().protocol().at(8), QLatin1String("getField: myBoolProperty Z"));
QCOMPARE(obj.jniHandle().protocol().at(9), QLatin1String("setField: myBoolProperty Z"));
#endif
}
};
......
......@@ -11,6 +11,7 @@
#include "jnimethod.h"
#include "jniobject.h"
#include "jnipp.h"
#include "jniprimitivetypes.h"
#include "jniproperty.h"
#include "jnireturnvalue.h"
#include "jnisignature.h"
......
......@@ -19,11 +19,12 @@ namespace Internal {
/** Call argument wrapper. */
template <typename T, typename = std::void_t<>>
struct argument {
typedef std::conditional_t<Jni::is_basic_type<T>::value, T, const Jni::Object<T>&> type;
static_assert(!is_invalid_primitive_type<T>::value, "Using an incompatible primitive type!");
typedef std::conditional_t<Jni::is_primitive_type<T>::value, T, const Jni::Object<T>&> type;
static inline constexpr auto toCallArgument(type value)
{
if constexpr (Jni::is_basic_type<T>::value) {
return value;
if constexpr (Jni::is_primitive_type<T>::value) {
return primitive_value<T>::toJni(value);
} else {
return value.jniHandle().object();
}
......
......@@ -23,7 +23,7 @@ namespace KAndroidExtras {
///@cond internal
namespace Internal {
/** Basic type array type traits. */
/** Primitive type array type traits. */
template <typename T> struct array_trait {
typedef jobjectArray type;
};
......@@ -49,7 +49,7 @@ MAKE_ARRAY_TRAIT(jdouble, Double)
#undef MAKE_ARRAY_TRAIT
/** Meta function for retrieving a JNI array .*/
template <typename Container, typename Value, bool is_basic> struct FromArray {};
template <typename Container, typename Value, bool is_primitive> struct FromArray {};
template <typename Container>
struct FromArray<Container, QAndroidJniObject, false>
......@@ -91,7 +91,7 @@ struct FromArray<Container, Value, false>
}
};
// specializations for basic types
// specializations for primitive types
template <typename Container, typename Value>
struct FromArray<Container, Value, true>
{
......@@ -116,7 +116,7 @@ struct FromArray<Container, Value, true>
}
};
// array wrapper, common base for basic and non-basic types
// array wrapper, common base for primitive and non-primitive types
template <typename T>
class ArrayImplBase {
public:
......@@ -152,13 +152,14 @@ protected:
QAndroidJniObject m_array;
};
template <typename T, bool is_basic>
template <typename T, bool is_primitive>
class ArrayImpl {};
// array wrapper for primitive types
template <typename T>
class ArrayImpl<T, true> : public ArrayImplBase<T>
{
static_assert(!Internal::is_invalid_primitive_type<T>::value, "Using an incompatible primitive type!");
public:
inline ArrayImpl(const QAndroidJniObject &array)
: ArrayImplBase<T>(array)
......@@ -303,16 +304,16 @@ namespace Jni {
/** Convert a JNI array to a C++ container.
* Container value types can be any of
* - QAndroidJniObject
* - a basic JNI type
* - a primitive JNI type
* - a type with a conversion defined with @c JNI_DECLARE_CONVERTER
*/
template <typename Container> constexpr __attribute__((__unused__)) Internal::FromArray<Container, typename Container::value_type, Jni::is_basic_type<typename Container::value_type>::value> fromArray = {};
template <typename Container> constexpr __attribute__((__unused__)) Internal::FromArray<Container, typename Container::value_type, Jni::is_primitive_type<typename Container::value_type>::value> fromArray = {};
/** Container-like wrapper for JNI arrays. */
template <typename T>
class Array : public Internal::ArrayImpl<T, Jni::is_basic_type<T>::value> {
class Array : public Internal::ArrayImpl<T, Jni::is_primitive_type<T>::value> {
public:
using Internal::ArrayImpl<T, Jni::is_basic_type<T>::value>::ArrayImpl;
using Internal::ArrayImpl<T, Jni::is_primitive_type<T>::value>::ArrayImpl;
template <typename Container>
inline operator Container() const {
// ### should this be re-implemented in terms of Jni::Array API rather than direct JNI access?
......
......@@ -41,11 +41,14 @@ namespace Internal {
// method call wrapper
template <typename RetT>
struct caller {
static_assert(!is_invalid_primitive_type<RetT>::value, "Using an incompatible primitive type!");
template <typename ...Args>
static auto call(const QAndroidJniObject &handle, const char *name, const char *signature, Args&&... args)
{
if constexpr (Jni::is_basic_type<RetT>::value) {
return handle.callMethod<RetT>(name, signature, std::forward<Args>(args)...);
if constexpr (std::is_same_v<RetT, void>) {
handle.callMethod<RetT>(name, signature, std::forward<Args>(args)...);
} else if constexpr (Jni::is_primitive_type<RetT>::value) {
return Internal::return_wrapper<RetT>::toReturnValue(handle.callMethod<typename primitive_value<RetT>::JniType>(name, signature, std::forward<Args>(args)...));
} else {
return Internal::return_wrapper<RetT>::toReturnValue(handle.callObjectMethod(name, signature, std::forward<Args>(args)...));
}
......@@ -53,8 +56,10 @@ namespace Internal {
static auto call(const QAndroidJniObject &handle, const char *name, const char *signature)
{
if constexpr (Jni::is_basic_type<RetT>::value) {
if constexpr (std::is_same_v<RetT, void>) {
return handle.callMethod<RetT>(name, signature);
} else if constexpr (Jni::is_primitive_type<RetT>::value) {
return Internal::return_wrapper<RetT>::toReturnValue(handle.callMethod<typename primitive_value<RetT>::JniType>(name, signature));
} else {
return Internal::return_wrapper<RetT>::toReturnValue(handle.callObjectMethod(name, signature));
}
......@@ -64,19 +69,24 @@ namespace Internal {
// static method call wrapper
template <typename RetT>
struct static_caller {
static_assert(!is_invalid_primitive_type<RetT>::value, "Using an incompatible primitive type!");
template <typename ...Args>
static auto call(const char *className, const char *name, const char *signature, Args&&... args)
{
if constexpr (Jni::is_basic_type<RetT>::value) {
if constexpr (std::is_same_v<RetT, void>) {
return QAndroidJniObject::callStaticMethod<RetT>(className, name, signature, std::forward<Args>(args)...);
} else if constexpr (Jni::is_primitive_type<RetT>::value) {
return Internal::return_wrapper<RetT>::toReturnValue(QAndroidJniObject::callStaticMethod<typename primitive_value<RetT>::JniType>(className, name, signature, std::forward<Args>(args)...));
} else {
return Internal::return_wrapper<RetT>::toReturnValue(QAndroidJniObject::callStaticObjectMethod(className, name, signature, std::forward<Args>(args)...));
}
}
static auto call(const char *className, const char *name, const char *signature)
{
if constexpr (Jni::is_basic_type<RetT>::value) {
if constexpr (std::is_same_v<RetT, void>) {
return QAndroidJniObject::callStaticMethod<RetT>(className, name, signature);
} else if constexpr (Jni::is_primitive_type<RetT>::value) {
return Internal::return_wrapper<RetT>::toReturnValue(QAndroidJniObject::callStaticMethod<typename primitive_value<RetT>::JniType>(className, name, signature));
} else {
return Internal::return_wrapper<RetT>::toReturnValue(QAndroidJniObject::callStaticObjectMethod(className, name, signature));
}
......@@ -89,14 +99,14 @@ namespace Internal {
* Wrap a JNI method call.
* This will add a 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
* - primitive types have to match exactly
* - non-primitive 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
* - primitive types are returned directly
* - non-primitive types without a registered type conversion are returned as @c QAndroidJniObject.
* - non-primitive 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.
......@@ -105,9 +115,9 @@ namespace Internal {
*
* Thie macro can only be placed in classes having the @c JNI_OBJECT macro.
*
* @param RetT The return type. Must either be a basic type or a type declared with @c JNI_TYPE
* @param RetT The return type. Must either be a primitive 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
* @param Args A list or argument types (can be empty). Must either be primitive types or types declared
* with @c JNI_TYPE.
*/
#define JNI_METHOD(RetT, Name, ...) \
......@@ -120,14 +130,14 @@ inline auto Name( JNI_PARAMS(__VA_ARGS__) ) const { \
* 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
* - primitive types have to match exactly
* - non-primitive 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
* - primitive types are returned directly
* - non-primitive types without a registered type conversion are returned as @c QAndroidJniObject.
* - non-primitive 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.
......@@ -136,9 +146,9 @@ inline auto Name( JNI_PARAMS(__VA_ARGS__) ) const { \
*
* Thie macro can only be placed in classes having the @c JNI_UNMANAGED_OBJECT or @c JNI_OBJECT macro.
*
* @param RetT The return type. Must either be a basic type or a type declared with @c JNI_TYPE
* @param RetT The return type. Must either be a primitive 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
* @param Args A list or argument types (can be empty). Must either be primitive types or types declared
* with @c JNI_TYPE.
*/
#define JNI_STATIC_METHOD(RetT, Name, ...) \
......@@ -151,14 +161,14 @@ static inline auto Name( JNI_PARAMS(__VA_ARGS__) ) { \
* Wrap a JNI constructor call.
* This will add a constructor 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
* - primitive types have to match exactly
* - non-primitive types can be either passed as @c QAndroidJniObject instance or with a type that has an
* conversion registered with @c JNI_DECLARE_CONVERTER.
*
* Thie macro can only be placed in classes having @c JNI_OBJECT macro.
*
* @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
* @param Args A list or argument types (can be empty). Must either be primitive types or types declared
* with @c JNI_TYPE.
*/
#define JNI_CONSTRUCTOR(Name, ...) \
......
/*
SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIPRIMITIVETYPES_H
#define KANDROIDEXTRAS_JNIPRIMITIVETYPES_H
#include <jni.h>
#include <type_traits>
namespace KAndroidExtras {
namespace Jni {
/** Type trait to check whether @tparam T is a primitive JNI type or an object type. */
template <typename T> struct is_primitive_type : std::false_type {};
template <> struct is_primitive_type<jboolean> : std::true_type {};
template <> struct is_primitive_type<jbyte> : std::true_type {};
template <> struct is_primitive_type<jchar> : std::true_type {};
template <> struct is_primitive_type<jshort> : std::true_type {};
template <> struct is_primitive_type<jint> : std::true_type {};
template <> struct is_primitive_type<jlong> : std::true_type {};
template <> struct is_primitive_type<jfloat> : std::true_type {};
template <> struct is_primitive_type<jdouble> : std::true_type {};
template <> struct is_primitive_type<void> : std::true_type {};
// special case for bool <-> jboolean conversion
template <> struct is_primitive_type<bool> : std::true_type {};
}
namespace Internal {
/** Utility trait to check for basic C++ types that are not JNI primitive types. */
template <typename T> struct is_invalid_primitive_type : std::false_type {};
template <> struct is_invalid_primitive_type<uint8_t> : std::true_type {};
template <> struct is_invalid_primitive_type<char> : std::true_type {};
template <> struct is_invalid_primitive_type<uint32_t> : std::true_type {};
template <> struct is_invalid_primitive_type<uint64_t> : std::true_type {};
// C++ primitive to JNI primitive conversion
template <typename T>
struct primitive_value {
static_assert(!is_invalid_primitive_type<T>::value, "Using an incompatible primitive type!");
typedef T JniType;
typedef T NativeType;
static constexpr inline T toJni(T value) { return value; }
static constexpr inline T fromJni(T value) { return value; }
};
template <> struct primitive_value<void> {};
template <>
struct primitive_value<jboolean> {
typedef jboolean JniType;
typedef bool NativeType;
static constexpr inline jboolean toJni(bool value) { return value ? 1 : 0; }
static constexpr inline bool fromJni(jboolean value) { return value != 0; }
};
template <>
struct primitive_value<bool> {
typedef jboolean JniType;
typedef bool NativeType;
static constexpr inline jboolean toJni(bool value) { return value ? 1 : 0; }
static constexpr inline bool fromJni(jboolean value) { return value != 0; }
};
}
}
#endif
......@@ -25,9 +25,10 @@ template <typename T> class Array;
namespace Internal {
/** Wrapper for static properties. */
template <typename PropType, typename ClassType, typename NameHolder, bool BasicType> struct StaticProperty {};
template <typename PropType, typename ClassType, typename NameHolder, bool PrimitiveType> struct StaticProperty {};
template <typename PropType, typename ClassType, typename NameHolder>
struct StaticProperty<PropType, ClassType, NameHolder, false> {
static_assert(!is_invalid_primitive_type<PropType>::value, "Using an incompatible primitive type!");
inline QAndroidJniObject get() const
{
return QAndroidJniObject::getStaticObjectField(Jni::typeName<ClassType>(), Jni::typeName<NameHolder>(), Jni::signature<PropType>());
......@@ -49,9 +50,9 @@ struct StaticProperty<PropType, ClassType, NameHolder, false> {
template <typename PropType, typename ClassType, typename NameHolder>
struct StaticProperty<PropType, ClassType, NameHolder, true> {
inline operator PropType() const
inline operator auto() const
{
return QAndroidJniObject::getStaticField<PropType>(Jni::typeName<ClassType>(), Jni::typeName<NameHolder>());
return primitive_value<PropType>::fromJni(QAndroidJniObject::getStaticField<typename primitive_value<PropType>::JniType>(Jni::typeName<ClassType>(), Jni::typeName<NameHolder>()));
}
};
......@@ -66,11 +67,12 @@ protected:
};
/** Wrapper for non-static properties. */
template <typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder, bool BasicType> struct Property {};
template <typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder, bool PrimitiveType> struct Property {};
template <typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder>
class Property<PropType, ClassType, NameHolder, OffsetHolder, false> : public PropertyBase<ClassType, OffsetHolder> {
private:
struct _jni_NoType {};
static_assert(!is_invalid_primitive_type<PropType>::value, "Using an incompatible primitive type!");
public:
inline QAndroidJniObject get() const
{
......@@ -115,13 +117,13 @@ public:
template <typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder>
class Property<PropType, ClassType, NameHolder, OffsetHolder, true> : public PropertyBase<ClassType, OffsetHolder> {
public:
inline operator PropType() const
inline operator auto() const
{
return this->handle().template getField<PropType>(Jni::typeName<NameHolder>());
return primitive_value<PropType>::fromJni(this->handle().template getField<typename primitive_value<PropType>::JniType>(Jni::typeName<NameHolder>()));
}
inline Property& operator=(PropType value)
{
this->handle().setField(Jni::typeName<NameHolder>(), value);
this->handle().setField(Jni::typeName<NameHolder>(), primitive_value<PropType>::toJni(value));
return *this;
}
};
......@@ -154,7 +156,7 @@ public:
private: \
struct _jni_ ## name ## __NameHolder { static constexpr const char* jniName() { return "" #name; } }; \
public: \
static inline const KAndroidExtras::Internal::StaticProperty<type, _jni_ThisType, _jni_ ## name ## __NameHolder, Jni::is_basic_type<type>::value> name;
static inline const KAndroidExtras::Internal::StaticProperty<type, _jni_ThisType, _jni_ ## name ## __NameHolder, Jni::is_primitive_type<type>::value> name;
/**
* Wrap a member property.
......@@ -181,7 +183,7 @@ private: \
}; \
friend class KAndroidExtras::Internal::PropertyBase<_jni_ThisType, _jni_ ## name ## __OffsetHolder>; \
public: \
[[no_unique_address]] KAndroidExtras::Internal::Property<type, _jni_ThisType, _jni_ ## name ## __NameHolder, _jni_ ## name ## __OffsetHolder, KAndroidExtras::Jni::is_basic_type<type>::value> name;
[[no_unique_address]] KAndroidExtras::Internal::Property<type, _jni_ThisType, _jni_ ## name ## __NameHolder, _jni_ ## name ## __OffsetHolder, KAndroidExtras::Jni::is_primitive_type<type>::value> name;
}
#endif // KANDROIDEXTRAS_JNIPROPERTIES_H
......@@ -19,17 +19,22 @@ namespace Internal {
/** Return value wrapping helper. */
template <typename RetT, typename = std::void_t<>>
class return_wrapper {
static inline constexpr bool is_basic = Jni::is_basic_type<RetT>::value;
static_assert(!is_invalid_primitive_type<RetT>::value, "Using an incompatible primitive type!");
static inline constexpr bool is_primitive = Jni::is_primitive_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, RetT, QAndroidJniObject> JniReturnT;
typedef std::conditional_t<is_primitive, RetT, QAndroidJniObject> JniReturnT;
public:
static inline constexpr auto toReturnValue(JniReturnT value)
{
if constexpr (is_convertible) {
return Jni::Object<RetT>(value);
} else {
}
else if constexpr (is_primitive) {
return primitive_value<RetT>::fromJni(value);
}
else {
return value;
}
}
......
......@@ -71,16 +71,17 @@ struct JniSignature
}
};
template <> struct JniSignature<bool> { constexpr inline auto operator()() const { return StaticString<'Z'>(); } };
template <> struct JniSignature<jboolean> { constexpr inline auto operator()() const { return StaticString<'Z'>(); } };
template <> struct JniSignature<jbyte> { constexpr inline auto operator()() const { return StaticString<'B'>(); } };
template <> struct JniSignature<char> { constexpr inline auto operator()() const { return StaticString<'C'>(); } };
template <> struct JniSignature<short> { constexpr inline auto operator()() const { return StaticString<'S'>(); } };
template <> struct JniSignature<int> { constexpr inline auto operator()() const { return StaticString<'I'>(); } };
template <> struct JniSignature<jchar> { constexpr inline auto operator()() const { return StaticString<'C'>(); } };
template <> struct JniSignature<jshort> { constexpr inline auto operator()() const { return StaticString<'S'>(); } };
template <> struct JniSignature<jint> { constexpr inline auto operator()() const { return StaticString<'I'>(); } };
template <> struct JniSignature<jlong> { constexpr inline auto operator()() const { return StaticString<'J'>(); } };
template <> struct JniSignature<float> { constexpr inline auto operator()() const { return StaticString<'F'>(); } };
template <> struct JniSignature<double> { constexpr inline auto operator()() const { return StaticString<'D'>(); } };
template <> struct JniSignature<jfloat> { constexpr inline auto operator()() const { return StaticString<'F'>(); } };
template <> struct JniSignature<jdouble> { constexpr inline auto operator()() const { return StaticString<'D'>(); } };
template <> struct JniSignature<void> { constexpr inline auto operator()() const { return StaticString<'V'>(); } };
// special case due to jboolean != bool
template <> struct JniSignature<bool> { constexpr inline auto operator()() const { return StaticString<'Z'>(); } };
template <typename T>
struct JniSignature<T*>
......
......@@ -7,6 +7,7 @@
#ifndef KANDROIDEXTRAS_JNITYPETRAITS_H
#define KANDROIDEXTRAS_JNITYPETRAITS_H
#include "jniprimitivetypes.h"
#include "jnitypes.h"
#include <qglobal.h>
......@@ -17,29 +18,12 @@
using QAndroidJniObject = QJniObject;
#endif
#include <type_traits>
namespace KAndroidExtras {
namespace Jni {
template <typename T> class Array;
/** Type trait to check whether @tparam T is a basic JNI type or an object type.
* Those two typically need different handling both with JNI API and with QAndroidJniObject API.
*/
template <typename T> struct is_basic_type : std::false_type {};
template <> struct is_basic_type<bool> : std::true_type {};
template <> struct is_basic_type<jboolean> : std::true_type {};
template <> struct is_basic_type<jbyte> : std::true_type {};
template <> struct is_basic_type<jchar> : std::true_type {};
template <> struct is_basic_type<jshort> : std::true_type {};
template <> struct is_basic_type<jint> : std::true_type {};
template <> struct is_basic_type<jlong> : std::true_type {};
template <> struct is_basic_type<jfloat> : std::true_type {};
template <> struct is_basic_type<jdouble> : std::true_type {};
template <> struct is_basic_type<void> : std::true_type {};
/** Type conversion trait, see @c JNI_DECLARE_CONVERTER. */
template <typename T> struct converter {
typedef void type;
......@@ -59,7 +43,7 @@ template <typename T> struct is_object_wrapper<T, std::void_t<typename T::_jni_T