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