// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QJNITYPES_IMPL_H #define QJNITYPES_IMPL_H #include #include #if defined(Q_QDOC) || defined(Q_OS_ANDROID) #include QT_BEGIN_NAMESPACE namespace QtJniTypes { // a constexpr type for string literals of any character width, aware of the length // of the string. template struct CTString { BaseType m_data[N_WITH_NULL] = {}; constexpr CTString() noexcept {} // Can be instantiated (only) with a string literal constexpr explicit CTString(const BaseType (&data)[N_WITH_NULL]) noexcept { for (size_t i = 0; i < N_WITH_NULL - 1; ++i) m_data[i] = data[i]; } constexpr BaseType at(size_t i) const { return m_data[i]; } constexpr BaseType operator[](size_t i) const { return at(i); } static constexpr size_t size() noexcept { return N_WITH_NULL; } constexpr operator const BaseType *() const noexcept { return m_data; } constexpr const BaseType *data() const noexcept { return m_data; } template constexpr bool startsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept { if constexpr (N2_WITH_NULL > N_WITH_NULL) { return false; } else { for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) { if (m_data[i] != lit[i]) return false; } } return true; } constexpr bool startsWith(BaseType c) const noexcept { return N_WITH_NULL > 1 && m_data[0] == c; } template constexpr bool endsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept { if constexpr (N2_WITH_NULL > N_WITH_NULL) { return false; } else { for (size_t i = 0; i < N2_WITH_NULL; ++i) { if (m_data[N_WITH_NULL - i - 1] != lit[N2_WITH_NULL - i - 1]) return false; } } return true; } constexpr bool endsWith(BaseType c) const noexcept { return N_WITH_NULL > 1 && m_data[N_WITH_NULL - 2] == c; } template friend inline constexpr bool operator==(const CTString &lhs, const CTString &rhs) noexcept { if constexpr (N_WITH_NULL != N2_WITH_NULL) { return false; } else { for (size_t i = 0; i < N_WITH_NULL - 1; ++i) { if (lhs.at(i) != rhs.at(i)) return false; } } return true; } template friend inline constexpr bool operator!=(const CTString &lhs, const CTString &rhs) noexcept { return !operator==(lhs, rhs); } template friend inline constexpr bool operator==(const CTString &lhs, const BaseType (&rhs)[N2_WITH_NULL]) noexcept { return operator==(lhs, CTString(rhs)); } template friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL], const CTString &rhs) noexcept { return operator==(CTString(lhs), rhs); } template friend inline constexpr bool operator!=(const CTString &lhs, const BaseType (&rhs)[N2_WITH_NULL]) noexcept { return operator!=(lhs, CTString(rhs)); } template friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL], const CTString &rhs) noexcept { return operator!=(CTString(lhs), rhs); } template friend inline constexpr auto operator+(const CTString &lhs, const CTString &rhs) noexcept { char data[N_WITH_NULL + N2_WITH_NULL - 1] = {}; for (size_t i = 0; i < N_WITH_NULL - 1; ++i) data[i] = lhs[i]; for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) data[N_WITH_NULL - 1 + i] = rhs[i]; return CTString(data); } }; // Helper types that allow us to disable variadic overloads that would conflict // with overloads that take a const char*. template struct IsStringType : std::false_type {}; template<> struct IsStringType : std::true_type {}; template<> struct IsStringType : std::true_type {}; template struct IsStringType> : std::true_type {}; template struct IsStringType : std::true_type {}; template struct IsStringType : std::true_type {}; template struct IsStringType : std::true_type {}; template struct Traits { // The return type of className/signature becomes void for any type // not handled here. This indicates that the Traits type is not specialized // for the respective type, which we use to detect invalid types in the // IfValidSignatureTypes and IfValidFieldType predicates below. static constexpr auto className() { if constexpr (std::is_same_v) return CTString("java/lang/String"); else if constexpr (std::is_same_v) return CTString("java/lang/Object"); else if constexpr (std::is_same_v) return CTString("java/lang/Class"); else if constexpr (std::is_same_v) return CTString("java/lang/Throwable"); // else: return void -> not implemented } static constexpr auto signature() { if constexpr (!std::is_same_v) { // the type signature of any object class is L; return CTString("L") + className() + CTString(";"); } else if constexpr (std::is_array_v) { using UnderlyingType = typename std::remove_extent_t; static_assert(!std::is_array_v, "Traits::signature() does not handle multi-dimensional arrays"); return CTString("[") + Traits::signature(); } else if constexpr (std::is_same_v) { return CTString("[Ljava/lang/Object;"); } else if constexpr (std::is_same_v) { return CTString("[Z"); } else if constexpr (std::is_same_v) { return CTString("[B"); } else if constexpr (std::is_same_v) { return CTString("[S"); } else if constexpr (std::is_same_v) { return CTString("[I"); } else if constexpr (std::is_same_v) { return CTString("[J"); } else if constexpr (std::is_same_v) { return CTString("[F"); } else if constexpr (std::is_same_v) { return CTString("[D"); } else if constexpr (std::is_same_v) { return CTString("[C"); } else if constexpr (std::is_same_v) { return CTString("Z"); } else if constexpr (std::is_same_v) { return CTString("Z"); } else if constexpr (std::is_same_v) { return CTString("B"); } else if constexpr (std::is_same_v) { return CTString("C"); } else if constexpr (std::is_same_v) { return CTString("C"); } else if constexpr (std::is_same_v) { return CTString("S"); } else if constexpr (std::is_same_v) { return CTString("S"); } else if constexpr (std::is_same_v) { return CTString("I"); } else if constexpr (std::is_same_v) { return CTString("I"); } else if constexpr (std::is_same_v) { return CTString("I"); } else if constexpr (std::is_same_v) { return CTString("J"); } else if constexpr (std::is_same_v) { return CTString("J"); } else if constexpr (std::is_same_v) { return CTString("F"); } else if constexpr (std::is_same_v) { return CTString("F"); } else if constexpr (std::is_same_v) { return CTString("D"); } else if constexpr (std::is_same_v) { return CTString("D"); } else if constexpr (std::is_same_v) { return CTString("V"); } else if constexpr (std::is_enum_v) { return Traits>::signature(); } else if constexpr (std::is_same_v) { return CTString("Ljava/lang/String;"); } // else: return void -> not implemented } }; template static constexpr bool sameTypeForJni = (QtJniTypes::Traits::signature() == QtJniTypes::Traits::signature()) && (sizeof(Have) == sizeof(Want)); template struct Caller {}; #define MAKE_CALLER(Type, Method) \ template \ struct Caller>> \ { \ static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, va_list args) \ { \ res = T(env->Call##Method##MethodV(obj, id, args)); \ } \ static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz, jmethodID id, va_list args) \ { \ res = T(env->CallStatic##Method##MethodV(clazz, id, args)); \ } \ static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id) \ { \ res = T(env->Get##Method##Field(obj, id)); \ } \ static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, jfieldID id) \ { \ res = T(env->GetStatic##Method##Field(clazz, id)); \ } \ static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, T value) \ { \ env->Set##Method##Field(obj, id, static_cast(value)); \ } \ static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, T value) \ { \ env->SetStatic##Method##Field(clazz, id, static_cast(value)); \ } \ } MAKE_CALLER(jboolean, Boolean); MAKE_CALLER(jbyte, Byte); MAKE_CALLER(jchar, Char); MAKE_CALLER(jshort, Short); MAKE_CALLER(jint, Int); MAKE_CALLER(jlong, Long); MAKE_CALLER(jfloat, Float); MAKE_CALLER(jdouble, Double); #undef MAKE_CALLER template static constexpr bool isPrimitiveType() { return Traits::signature().size() == 2; } template static constexpr bool isArrayType() { constexpr auto signature = Traits::signature(); return signature.startsWith('[') && signature.size() > 2; } template static constexpr bool isObjectType() { if constexpr (std::is_convertible_v) { return true; } else { constexpr auto signature = Traits::signature(); return (signature.startsWith('L') && signature.endsWith(';')) || isArrayType(); } } template static constexpr void assertObjectType() { static_assert(isObjectType(), "Type needs to be a JNI object type (convertible to jobject, or with " "an object type signature registered)!"); } // A set of types is valid if Traits::signature is implemented for all of them template constexpr bool ValidSignatureTypesDetail = !std::disjunction::signature()), void>..., IsStringType...>::value; template using IfValidSignatureTypes = std::enable_if_t< ValidSignatureTypesDetail...>, bool>; template constexpr bool ValidFieldTypeDetail = isObjectType() || isPrimitiveType(); template using IfValidFieldType = std::enable_if_t< ValidFieldTypeDetail>, bool>; template = true> static constexpr auto methodSignature() { return (CTString("(") + ... + Traits>::signature()) + CTString(")") + Traits::signature(); } template = true> static constexpr auto fieldSignature() { return QtJniTypes::Traits::signature(); } template = true> static constexpr auto constructorSignature() { return methodSignature(); } template = true> static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...)) { return methodSignature(); } template = true> static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...)) { return methodSignature(); } } // namespace QtJniTypes QT_END_NAMESPACE #endif #endif // QJNITYPES_IMPL_H