// 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_H #define QJNITYPES_H #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 String { BaseType m_data[N_WITH_NULL] = {}; constexpr String() noexcept {} // Can be instantiated (only) with a string literal constexpr explicit String(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 String &lhs, const String &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 String &lhs, const String &rhs) noexcept { return !operator==(lhs, rhs); } template friend inline constexpr bool operator==(const String &lhs, const BaseType (&rhs)[N2_WITH_NULL]) noexcept { return operator==(lhs, String(rhs)); } template friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL], const String &rhs) noexcept { return operator==(String(lhs), rhs); } template friend inline constexpr bool operator!=(const String &lhs, const BaseType (&rhs)[N2_WITH_NULL]) noexcept { return operator!=(lhs, String(rhs)); } template friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL], const String &rhs) noexcept { return operator!=(String(lhs), rhs); } template friend inline constexpr auto operator+(const String &lhs, const String &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 String(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 static void staticAssertTypeMismatch() { static_assert(flag, "The used type is not supported by this template call. " "Use a JNI based type instead."); } template constexpr auto typeSignature() { if constexpr(std::is_same::value) return String("Ljava/lang/Object;"); else if constexpr(std::is_same::value) return String("Ljava/lang/Class;"); else if constexpr(std::is_same::value) return String("Ljava/lang/String;"); else if constexpr(std::is_same::value) return String("[Ljava/lang/Object;"); else if constexpr(std::is_same::value) return String("Ljava/lang/Throwable;"); else if constexpr(std::is_same::value) return String("[Z"); else if constexpr(std::is_same::value) return String("[B"); else if constexpr(std::is_same::value) return String("[S"); else if constexpr(std::is_same::value) return String("[I"); else if constexpr(std::is_same::value) return String("[J"); else if constexpr(std::is_same::value) return String("[F"); else if constexpr(std::is_same::value) return String("[D"); else if constexpr(std::is_same::value) return String("[C"); else if constexpr(std::is_same::value) return String("Z"); else if constexpr(std::is_same::value) return String("Z"); else if constexpr(std::is_same::value) return String("B"); else if constexpr(std::is_same::value) return String("C"); else if constexpr(std::is_same::value) return String("C"); else if constexpr(std::is_same::value) return String("S"); else if constexpr(std::is_same::value) return String("S"); else if constexpr(std::is_same::value) return String("I"); else if constexpr(std::is_same::value) return String("I"); else if constexpr(std::is_same::value) return String("I"); else if constexpr(std::is_same::value) return String("J"); else if constexpr(std::is_same::value) return String("J"); else if constexpr(std::is_same::value) return String("F"); else if constexpr(std::is_same::value) return String("F"); else if constexpr(std::is_same::value) return String("D"); else if constexpr(std::is_same::value) return String("D"); else if constexpr(std::is_same::value) return String("V"); else if constexpr(IsStringType::value) static_assert(!IsStringType::value, "Don't use a literal type, call data!"); else staticAssertTypeMismatch(); } template static void staticAssertClassNotRegistered() { static_assert(flag, "Class not registered, use Q_DECLARE_JNI_CLASS"); } template constexpr auto className() { if constexpr(std::is_same::value) return String("java/lang/String"); else staticAssertClassNotRegistered(); } template static constexpr bool isPrimitiveType() { return typeSignature().size() == 2; } template static constexpr bool isObjectType() { if constexpr(std::is_convertible::value) { return true; } else { constexpr auto signature = typeSignature(); return (signature.startsWith('L') || signature.startsWith('[')) && signature.endsWith(';'); } } template static constexpr void assertPrimitiveType() { static_assert(isPrimitiveType(), "Type needs to be a primitive JNI type!"); } 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)!"); } template static constexpr void assertType() { static_assert(isPrimitiveType() || isObjectType(), "Type needs to be a JNI type!"); } template static constexpr auto methodSignature() { return (String("(") + ... + typeSignature>()) + String(")") + typeSignature(); } template static constexpr auto fieldSignature() { return QtJniTypes::typeSignature(); } template static constexpr auto constructorSignature() { return methodSignature(); } template static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...)) { return methodSignature(); } template static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...)) { return methodSignature(); } // A generic thin wrapper around jobject, convertible to jobject. // We need this as a baseclass so that QJniObject can be implicitly // constructed from the various subclasses - we can't provide an // operator QJniObject() here as the class is not declared. struct Object { jobject _object; constexpr operator jobject() const { return _object; } }; } // namespace QtJniTypes #define Q_DECLARE_JNI_TYPE_HELPER(Type) \ namespace QtJniTypes { \ struct Type : Object \ { \ constexpr Type(jobject o) noexcept : Object{o} {} \ }; \ } \ #define Q_DECLARE_JNI_TYPE(Type, Signature) \ Q_DECLARE_JNI_TYPE_HELPER(Type) \ template<> \ constexpr auto QtJniTypes::typeSignature() \ { \ static_assert((Signature[0] == 'L' || Signature[0] == '[') \ && Signature[sizeof(Signature) - 2] == ';', \ "Type signature needs to start with 'L' or '['" \ " and end with ';'"); \ return QtJniTypes::String(Signature); \ } \ #define Q_DECLARE_JNI_CLASS(Type, Signature) \ Q_DECLARE_JNI_TYPE_HELPER(Type) \ template<> \ constexpr auto QtJniTypes::className() \ { \ return QtJniTypes::String(Signature); \ } \ template<> \ constexpr auto QtJniTypes::typeSignature() \ { \ return QtJniTypes::String("L") \ + QtJniTypes::String(Signature) \ + QtJniTypes::String(";"); \ } \ #define Q_DECLARE_JNI_NATIVE_METHOD(...) \ QT_OVERLOADED_MACRO(QT_DECLARE_JNI_NATIVE_METHOD, __VA_ARGS__) \ #define QT_DECLARE_JNI_NATIVE_METHOD_2(Method, Name) \ namespace QtJniMethods { \ static constexpr auto Method##_signature = \ QtJniTypes::nativeMethodSignature(Method); \ static const JNINativeMethod Method##_method = { \ #Name, Method##_signature.data(), \ reinterpret_cast(Method) \ }; \ } \ #define QT_DECLARE_JNI_NATIVE_METHOD_1(Method) \ QT_DECLARE_JNI_NATIVE_METHOD_2(Method, Method) \ #define Q_JNI_NATIVE_METHOD(Method) QtJniMethods::Method##_method QT_END_NAMESPACE #endif #endif // QJNITYPES_H