File indexing completed on 2024-06-23 05:08:44
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 0006 #ifndef KANDROIDEXTRAS_JNIMETHOD_H 0007 #define KANDROIDEXTRAS_JNIMETHOD_H 0008 0009 #include "jniargument.h" 0010 #include "jniarray.h" 0011 #include "jniobject.h" 0012 #include "jnipp.h" 0013 #include "jnireturnvalue.h" 0014 #include "jnitypetraits.h" 0015 0016 namespace KAndroidExtras { 0017 ///@cond internal 0018 0019 // method parameter generation 0020 #define JNI_PARAM(Type, Name) typename KAndroidExtras::Internal::argument<Type>::type Name 0021 0022 #define JNI_PARAMS_0(accu, arg) 0023 #define JNI_PARAMS_1(accu, arg, ...) JNI_PARAM(arg, a1) 0024 #define JNI_PARAMS_2(accu, arg, ...) JNI_PARAM(arg, a2), JNI_PARAMS_1(accu, __VA_ARGS__) 0025 #define JNI_PARAMS_3(accu, arg, ...) JNI_PARAM(arg, a3), JNI_PARAMS_2(accu, __VA_ARGS__) 0026 0027 #define JNI_PARAMS_(N, accu, ...) JNI_PP_CONCAT(JNI_PARAMS_, N)(accu, __VA_ARGS__) 0028 #define JNI_PARAMS(...) JNI_PARAMS_(JNI_PP_NARG(__VA_ARGS__), "", __VA_ARGS__) 0029 0030 // method argument forwarding generation 0031 #define JNI_ARG(Type, Name) KAndroidExtras::Internal::argument<Type>::toCallArgument(Name) 0032 #define JNI_ARGS_0(accu, arg) 0033 #define JNI_ARGS_1(accu, arg, ...) JNI_ARG(arg, a1) 0034 #define JNI_ARGS_2(accu, arg, ...) JNI_ARG(arg, a2), JNI_ARGS_1(accu, __VA_ARGS__) 0035 #define JNI_ARGS_3(accu, arg, ...) JNI_ARG(arg, a3), JNI_ARGS_2(accu, __VA_ARGS__) 0036 0037 #define JNI_ARGS_(N, accu, ...) JNI_PP_CONCAT(JNI_ARGS_, N)(accu, __VA_ARGS__) 0038 #define JNI_ARGS(...) JNI_ARGS_(JNI_PP_NARG(__VA_ARGS__), "", __VA_ARGS__) 0039 0040 namespace Internal { 0041 // method call wrapper 0042 template <typename RetT> 0043 struct caller { 0044 static_assert(!is_invalid_primitive_type<RetT>::value, "Using an incompatible primitive type!"); 0045 template <typename ...Args> 0046 static auto call(const QJniObject &handle, const char *name, const char *signature, Args&&... args) 0047 { 0048 if constexpr (std::is_same_v<RetT, void>) { 0049 handle.callMethod<RetT>(name, signature, std::forward<Args>(args)...); 0050 } else if constexpr (Jni::is_primitive_type<RetT>::value) { 0051 return Internal::return_wrapper<RetT>::toReturnValue(handle.callMethod<typename primitive_value<RetT>::JniType>(name, signature, std::forward<Args>(args)...)); 0052 } else { 0053 return Internal::return_wrapper<RetT>::toReturnValue(handle.callObjectMethod(name, signature, std::forward<Args>(args)...)); 0054 } 0055 } 0056 0057 static auto call(const QJniObject &handle, const char *name, const char *signature) 0058 { 0059 if constexpr (std::is_same_v<RetT, void>) { 0060 return handle.callMethod<RetT>(name, signature); 0061 } else if constexpr (Jni::is_primitive_type<RetT>::value) { 0062 return Internal::return_wrapper<RetT>::toReturnValue(handle.callMethod<typename primitive_value<RetT>::JniType>(name, signature)); 0063 } else { 0064 return Internal::return_wrapper<RetT>::toReturnValue(handle.callObjectMethod(name, signature)); 0065 } 0066 } 0067 }; 0068 0069 // static method call wrapper 0070 template <typename RetT> 0071 struct static_caller { 0072 static_assert(!is_invalid_primitive_type<RetT>::value, "Using an incompatible primitive type!"); 0073 template <typename ...Args> 0074 static auto call(const char *className, const char *name, const char *signature, Args&&... args) 0075 { 0076 if constexpr (std::is_same_v<RetT, void>) { 0077 return QJniObject::callStaticMethod<RetT>(className, name, signature, std::forward<Args>(args)...); 0078 } else if constexpr (Jni::is_primitive_type<RetT>::value) { 0079 return Internal::return_wrapper<RetT>::toReturnValue(QJniObject::callStaticMethod<typename primitive_value<RetT>::JniType>(className, name, signature, std::forward<Args>(args)...)); 0080 } else { 0081 return Internal::return_wrapper<RetT>::toReturnValue(QJniObject::callStaticObjectMethod(className, name, signature, std::forward<Args>(args)...)); 0082 } 0083 } 0084 static auto call(const char *className, const char *name, const char *signature) 0085 { 0086 if constexpr (std::is_same_v<RetT, void>) { 0087 return QJniObject::callStaticMethod<RetT>(className, name, signature); 0088 } else if constexpr (Jni::is_primitive_type<RetT>::value) { 0089 return Internal::return_wrapper<RetT>::toReturnValue(QJniObject::callStaticMethod<typename primitive_value<RetT>::JniType>(className, name, signature)); 0090 } else { 0091 return Internal::return_wrapper<RetT>::toReturnValue(QJniObject::callStaticObjectMethod(className, name, signature)); 0092 } 0093 } 0094 }; 0095 } 0096 ///@endcond 0097 0098 /** 0099 * Wrap a JNI method call. 0100 * This will add a method named @p Name to the current class. Argument types are checked at compile time, 0101 * with the following inputs being accepted: 0102 * - primitive types have to match exactly 0103 * - non-primitive types can be either passed as @c QJniObject instance or with a type that has an 0104 * conversion registered with @c JNI_DECLARE_CONVERTER. 0105 * 0106 * The return type of the method is determined as follows: 0107 * - primitive types are returned directly 0108 * - non-primitive types without a registered type conversion are returned as @c QJniObject. 0109 * - non-primitive types with a registered type conversion are returned in a wrapper class that can 0110 * be implicitly converted either to the destination type of the conversion, or a @c QJniObject. 0111 * This allows to avoid type conversion when chaining calls for example, it however needs additional 0112 * care when used in combination with automatic type deduction. 0113 * - array return types also result in a wrapper class that can be implicitly converted to a sequential 0114 * container or a @p QJniObject representing the JNI array. 0115 * 0116 * Thie macro can only be placed in classes having the @c JNI_OBJECT macro. 0117 * 0118 * @param RetT The return type. Must either be a primitive type or a type declared with @c JNI_TYPE 0119 * @param Name The name of the method. Must match the JNI method to be called exactly. 0120 * @param Args A list or argument types (can be empty). Must either be primitive types or types declared 0121 * with @c JNI_TYPE. 0122 */ 0123 #define JNI_METHOD(RetT, Name, ...) \ 0124 inline auto Name( JNI_PARAMS(__VA_ARGS__) ) const { \ 0125 using namespace KAndroidExtras; \ 0126 return Internal::caller<RetT>::call(jniHandle(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>() __VA_OPT__(,) JNI_ARGS(__VA_ARGS__)); \ 0127 } 0128 0129 /** 0130 * Wrap a JNI static method call. 0131 * This will add a static method named @p Name to the current class. Argument types are checked at compile time, 0132 * with the following inputs being accepted: 0133 * - primitive types have to match exactly 0134 * - non-primitive types can be either passed as @c QJniObject instance or with a type that has an 0135 * conversion registered with @c JNI_DECLARE_CONVERTER. 0136 * 0137 * The return type of the method is determined as follows: 0138 * - primitive types are returned directly 0139 * - non-primitive types without a registered type conversion are returned as @c QJniObject. 0140 * - non-primitive types with a registered type conversion are returned in a wrapper class that can 0141 * be implicitly converted either to the destination type of the conversion, or a @c QJniObject. 0142 * This allows to avoid type conversion when chaining calls for example, it however needs additional 0143 * care when used in combination with automatic type deduction. 0144 * - array return types also result in a wrapper class that can be implicitly converted to a sequential 0145 * container or a @p QJniObject representing the JNI array. 0146 * 0147 * Thie macro can only be placed in classes having the @c JNI_UNMANAGED_OBJECT or @c JNI_OBJECT macro. 0148 * 0149 * @param RetT The return type. Must either be a primitive type or a type declared with @c JNI_TYPE 0150 * @param Name The name of the method. Must match the JNI method to be called exactly. 0151 * @param Args A list or argument types (can be empty). Must either be primitive types or types declared 0152 * with @c JNI_TYPE. 0153 */ 0154 #define JNI_STATIC_METHOD(RetT, Name, ...) \ 0155 static inline auto Name( JNI_PARAMS(__VA_ARGS__) ) { \ 0156 using namespace KAndroidExtras; \ 0157 return Internal::static_caller<RetT>::call(Jni::typeName<_jni_ThisType>(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>() __VA_OPT__(,) JNI_ARGS(__VA_ARGS__)); \ 0158 } 0159 0160 /** 0161 * Wrap a JNI constructor call. 0162 * This will add a constructor named @p Name to the current class. Argument types are checked at compile time, 0163 * with the following inputs being accepted: 0164 * - primitive types have to match exactly 0165 * - non-primitive types can be either passed as @c QJniObject instance or with a type that has an 0166 * conversion registered with @c JNI_DECLARE_CONVERTER. 0167 * 0168 * Thie macro can only be placed in classes having @c JNI_OBJECT macro. 0169 * 0170 * @param Name The name of the method. Must match the JNI method to be called exactly. 0171 * @param Args A list or argument types (can be empty). Must either be primitive types or types declared 0172 * with @c JNI_TYPE. 0173 */ 0174 #define JNI_CONSTRUCTOR(Name, ...) \ 0175 inline Name( JNI_PARAMS(__VA_ARGS__) ) { \ 0176 using namespace KAndroidExtras; \ 0177 setJniHandle(QJniObject(Jni::typeName<_jni_ThisType>(), (const char*)Jni::signature<void(__VA_ARGS__)>() __VA_OPT__(,) JNI_ARGS(__VA_ARGS__))); \ 0178 } 0179 0180 } 0181 0182 #endif