JNI: replace static_assert mandates with enable_if constraints

A function not being available if the parameter list doesn't meet the
requirements results in much better error messages than a function not
compiling (somewhere deep in the call tree, potentially) because of it.

Change-Id: If2c320736083a385232cc72f608bc4d61025627c
Reviewed-by: Juha Vuolle <juha.vuolle@qt.io>
Reviewed-by: Petri Virkkunen <petri.virkkunen@qt.io>
Reviewed-by: Zoltan Gera <zoltan.gera@qt.io>
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
This commit is contained in:
Volker Hilsheimer 2023-09-11 18:44:24 +02:00
parent 10d915277e
commit 7a27609d73
2 changed files with 142 additions and 60 deletions

View File

@ -63,13 +63,16 @@ public:
jclass objectClass() const;
QByteArray className() const;
template <typename Ret, typename ...Args>
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<Ret> = true
#endif
>
auto callMethod(const char *methodName, const char *signature, Args &&...args) const
{
if constexpr (QtJniTypes::isObjectType<Ret>()) {
return callObjectMethod(methodName, signature, std::forward<Args>(args)...);
} else {
QtJniTypes::assertPrimitiveType<Ret>();
QJniEnvironment env;
jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
if (id) {
@ -89,7 +92,11 @@ public:
}
}
template <typename Ret, typename ...Args>
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::ValidSignatureTypes<Ret, Args...> = true
#endif
>
auto callMethod(const char *methodName, Args &&...args) const
{
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
@ -100,7 +107,11 @@ public:
}
}
template <typename Ret, typename ...Args>
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::ValidSignatureTypes<Ret, Args...> = true
#endif
>
QJniObject callObjectMethod(const char *methodName, Args &&...args) const
{
QtJniTypes::assertObjectType<Ret>();
@ -126,13 +137,16 @@ public:
return callStaticMethod<Ret, Args...>(clazz, id, std::forward<Args>(args)...);
}
template <typename Ret, typename ...Args>
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<Ret> = true
#endif
>
static auto callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args)
{
if constexpr (QtJniTypes::isObjectType<Ret>()) {
return callStaticObjectMethod(clazz, methodId, std::forward<Args>(args)...);
} else {
QtJniTypes::assertPrimitiveType<Ret>();
QJniEnvironment env;
if (clazz && methodId) {
if constexpr (std::is_same_v<Ret, void>) {
@ -151,7 +165,11 @@ public:
}
}
template <typename Ret, typename ...Args>
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::ValidSignatureTypes<Ret, Args...> = true
#endif
>
static auto callStaticMethod(const char *className, const char *methodName, Args &&...args)
{
QJniEnvironment env;
@ -159,7 +177,11 @@ public:
return callStaticMethod<Ret, Args...>(clazz, methodName, std::forward<Args>(args)...);
}
template <typename Ret, typename ...Args>
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::ValidSignatureTypes<Ret, Args...> = true
#endif
>
static auto callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
{
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
@ -175,7 +197,11 @@ public:
static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...);
template <typename Ret, typename ...Args>
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::ValidSignatureTypes<Ret, Args...> = true
#endif
>
static QJniObject callStaticObjectMethod(const char *className, const char *methodName, Args &&...args)
{
QtJniTypes::assertObjectType<Ret>();
@ -183,7 +209,11 @@ public:
return callStaticObjectMethod(className, methodName, signature.data(), std::forward<Args>(args)...);
}
template <typename Ret, typename ...Args>
template <typename Ret, typename ...Args
#ifndef Q_QDOC
, QtJniTypes::ValidSignatureTypes<Ret, Args...> = true
#endif
>
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
{
QtJniTypes::assertObjectType<Ret>();
@ -191,12 +221,16 @@ public:
return callStaticObjectMethod(clazz, methodName, signature.data(), std::forward<Args>(args)...);
}
template <typename T> auto getField(const char *fieldName) const
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
auto getField(const char *fieldName) const
{
if constexpr (QtJniTypes::isObjectType<T>()) {
return getObjectField<T>(fieldName);
} else {
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
constexpr auto signature = QtJniTypes::fieldSignature<T>();
@ -210,13 +244,16 @@ public:
}
}
template <typename T>
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
static auto getStaticField(const char *className, const char *fieldName)
{
if constexpr (QtJniTypes::isObjectType<T>()) {
return getStaticObjectField<T>(className, fieldName);
} else {
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
T res{};
@ -238,13 +275,16 @@ public:
}
}
template <typename T>
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
static auto getStaticField(jclass clazz, const char *fieldName)
{
if constexpr (QtJniTypes::isObjectType<T>()) {
return getStaticObjectField<T>(clazz, fieldName);
} else {
QtJniTypes::assertPrimitiveType<T>();
QJniEnvironment env;
T res{};
constexpr auto signature = QtJniTypes::fieldSignature<T>();
@ -258,26 +298,36 @@ public:
}
}
template <typename Klass, typename T>
template <typename Klass, typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
static auto getStaticField(const char *fieldName)
{
return getStaticField<T>(QtJniTypes::className<Klass>(), fieldName);
}
template <typename T>
template <typename T
#ifndef Q_QDOC
, std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
#endif
>
QJniObject getObjectField(const char *fieldName) const
{
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getObjectField(fieldName, signature);
}
QJniObject getObjectField(const char *fieldName, const char *signature) const;
template <typename T>
template <typename T
#ifndef Q_QDOC
, std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
#endif
>
static QJniObject getStaticObjectField(const char *className, const char *fieldName)
{
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(className, fieldName, signature);
}
@ -286,10 +336,13 @@ public:
const char *fieldName,
const char *signature);
template <typename T>
template <typename T
#ifndef Q_QDOC
, std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
#endif
>
static QJniObject getStaticObjectField(jclass clazz, const char *fieldName)
{
QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(clazz, fieldName, signature);
}
@ -297,9 +350,13 @@ public:
static QJniObject getStaticObjectField(jclass clazz, const char *fieldName,
const char *signature);
template <typename T> void setField(const char *fieldName, T value)
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
void setField(const char *fieldName, T value)
{
QtJniTypes::assertType<T>();
QJniEnvironment env;
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
@ -309,10 +366,13 @@ public:
}
}
template <typename T>
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
void setField(const char *fieldName, const char *signature, T value)
{
QtJniTypes::assertType<T>();
QJniEnvironment env;
jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
if (id) {
@ -321,10 +381,13 @@ public:
}
}
template <typename T>
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
static void setStaticField(const char *className, const char *fieldName, T value)
{
QtJniTypes::assertType<T>();
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
if (!clazz)
@ -340,11 +403,14 @@ public:
env.checkAndClearExceptions();
}
template <typename T>
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
static void setStaticField(const char *className, const char *fieldName,
const char *signature, T value)
{
QtJniTypes::assertType<T>();
QJniEnvironment env;
jclass clazz = QJniObject::loadClass(className, env.jniEnv());
@ -359,11 +425,14 @@ public:
}
}
template <typename T>
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
static void setStaticField(jclass clazz, const char *fieldName,
const char *signature, T value)
{
QtJniTypes::assertType<T>();
QJniEnvironment env;
jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
@ -373,10 +442,13 @@ public:
}
}
template <typename T>
template <typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
static void setStaticField(jclass clazz, const char *fieldName, T value)
{
QtJniTypes::assertType<T>();
QJniEnvironment env;
constexpr auto signature = QtJniTypes::fieldSignature<T>();
jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
@ -386,7 +458,11 @@ public:
}
}
template <typename Klass, typename T>
template <typename Klass, typename T
#ifndef Q_QDOC
, QtJniTypes::ValidFieldType<T> = true
#endif
>
static void setStaticField(const char *fieldName, T value)
{
setStaticField(QtJniTypes::className<Klass>(), fieldName, value);
@ -401,9 +477,9 @@ public:
// This function takes ownership of the jobject and releases the local ref. before returning.
static QJniObject fromLocalRef(jobject lref);
template <typename T> QJniObject &operator=(T obj)
template <typename T>
QJniObject &operator=(T obj)
{
QtJniTypes::assertType<T>();
assign(static_cast<T>(obj));
return *this;
}

View File

@ -5,6 +5,7 @@
#define QJNITYPES_H
#include <QtCore/qglobal.h>
#include <QtCore/q20type_traits.h>
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
@ -213,11 +214,12 @@ constexpr auto typeSignature()
return String("D");
} else if constexpr (std::is_same_v<T, void>) {
return String("V");
} else if constexpr (IsStringType<T>::value) {
static_assert(!IsStringType<T>::value, "Don't use a literal type, call data!");
} else {
staticAssertTypeMismatch();
}
// else: The return type becomes void, indicating that the typeSignature
// template is not implemented for the respective type. We use this to
// detect invalid types in the ValidSignatureTypes and ValidFieldType
// predicates below.
}
template<bool flag = false>
@ -260,12 +262,6 @@ static constexpr bool isArrayType()
return signature.startsWith('[');
}
template<typename T>
static constexpr void assertPrimitiveType()
{
static_assert(isPrimitiveType<T>(), "Type needs to be a primitive JNI type!");
}
template<typename T>
static constexpr void assertObjectType()
{
@ -274,41 +270,51 @@ static constexpr void assertObjectType()
"an object type signature registered)!");
}
template<typename T>
static constexpr void assertType()
{
static_assert(isPrimitiveType<T>() || isObjectType<T>(),
"Type needs to be a JNI type!");
}
// A set of types is valid if typeSignature is implemented for all of them
template<typename ...Types>
constexpr bool ValidSignatureTypesDetail = !std::disjunction<std::is_same<
decltype(QtJniTypes::typeSignature<Types>()),
void>...,
IsStringType<Types>...>::value;
template<typename ...Types>
using ValidSignatureTypes = std::enable_if_t<
ValidSignatureTypesDetail<q20::remove_cvref_t<Types>...>, bool>;
template<typename R, typename ...Args>
template<typename Type>
constexpr bool ValidFieldTypeDetail = isObjectType<Type>() || isPrimitiveType<Type>();
template<typename Type>
using ValidFieldType = std::enable_if_t<
ValidFieldTypeDetail<q20::remove_cvref_t<Type>>, bool>;
template<typename R, typename ...Args, ValidSignatureTypes<R, Args...> = true>
static constexpr auto methodSignature()
{
return (String("(") +
... + typeSignature<std::decay_t<Args>>())
... + typeSignature<q20::remove_cvref_t<Args>>())
+ String(")")
+ typeSignature<R>();
}
template<typename T>
template<typename T, ValidSignatureTypes<T> = true>
static constexpr auto fieldSignature()
{
return QtJniTypes::typeSignature<T>();
}
template<typename ...Args>
template<typename ...Args, ValidSignatureTypes<Args...> = true>
static constexpr auto constructorSignature()
{
return methodSignature<void, Args...>();
}
template<typename Ret, typename ...Args>
template<typename Ret, typename ...Args, ValidSignatureTypes<Ret, Args...> = true>
static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...))
{
return methodSignature<Ret, Args...>();
}
template<typename Ret, typename ...Args>
template<typename Ret, typename ...Args, ValidSignatureTypes<Ret, Args...> = true>
static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...))
{
return methodSignature<Ret, Args...>();