JNI: treat enums as their underlying types
Android APIs use integer constants like enum values, so they are mapped to one of the integeral types (jint, jshort, jlong etc) on the C++ side. Enable C++ code to declare an equivalent enum (scoped or unscoped), and to use that enum as a type in JNI calls by treating it as the underlying type in the signature() generator. Add a helper type trait that maps enums to their underlying type and other integral types to themselves (we can't use std::underlying_type_t on a non-enum type). Add tests. Note: Java Enums are special classes with fields; this change does not add any special support for those. Change-Id: Iec430a1553152dcf7a24209aaebbeceb1c6e38a8 Reviewed-by: Petri Virkkunen <petri.virkkunen@qt.io> Reviewed-by: Zoltan Gera <zoltan.gera@qt.io> Reviewed-by: Juha Vuolle <juha.vuolle@qt.io>
This commit is contained in:
parent
9965630aaf
commit
941f49b018
@ -531,6 +531,21 @@ private:
|
||||
friend bool operator==(const QJniObject &, const QJniObject &);
|
||||
friend bool operator!=(const QJniObject&, const QJniObject&);
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct IntegerTypeDetail
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
template<typename T>
|
||||
struct IntegerTypeDetail<T, typename std::enable_if_t<std::is_enum_v<T>>>
|
||||
{
|
||||
using type = std::underlying_type_t<T>;
|
||||
};
|
||||
|
||||
template<typename Have, typename Want>
|
||||
static constexpr bool likeIntegerType = std::is_same_v<Have, Want>
|
||||
|| std::is_same_v<typename IntegerTypeDetail<Have>::type, Want>;
|
||||
|
||||
template<typename T>
|
||||
static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj,
|
||||
jmethodID id, ...)
|
||||
@ -540,16 +555,16 @@ private:
|
||||
|
||||
if constexpr (std::is_same_v<T, jboolean>)
|
||||
res = env->CallBooleanMethodV(obj, id, args);
|
||||
else if constexpr (std::is_same_v<T, jbyte>)
|
||||
res = env->CallByteMethodV(obj, id, args);
|
||||
else if constexpr (std::is_same_v<T, jchar>)
|
||||
res = env->CallCharMethodV(obj, id, args);
|
||||
else if constexpr (std::is_same_v<T, jshort>)
|
||||
res = env->CallShortMethodV(obj, id, args);
|
||||
else if constexpr (std::is_same_v<T, jint>)
|
||||
res = env->CallIntMethodV(obj, id, args);
|
||||
else if constexpr (std::is_same_v<T, jlong>)
|
||||
res = env->CallLongMethodV(obj, id, args);
|
||||
else if constexpr (likeIntegerType<T, jbyte>)
|
||||
res = T{env->CallByteMethodV(obj, id, args)};
|
||||
else if constexpr (likeIntegerType<T, jchar>)
|
||||
res = T{env->CallCharMethodV(obj, id, args)};
|
||||
else if constexpr (likeIntegerType<T, jshort>)
|
||||
res = T{env->CallShortMethodV(obj, id, args)};
|
||||
else if constexpr (likeIntegerType<T, jint>)
|
||||
res = T{env->CallIntMethodV(obj, id, args)};
|
||||
else if constexpr (likeIntegerType<T, jlong>)
|
||||
res = T{env->CallLongMethodV(obj, id, args)};
|
||||
else if constexpr (std::is_same_v<T, jfloat>)
|
||||
res = env->CallFloatMethodV(obj, id, args);
|
||||
else if constexpr (std::is_same_v<T, jdouble>)
|
||||
@ -569,16 +584,16 @@ private:
|
||||
va_start(args, id);
|
||||
if constexpr (std::is_same_v<T, jboolean>)
|
||||
res = env->CallStaticBooleanMethodV(clazz, id, args);
|
||||
else if constexpr (std::is_same_v<T, jbyte>)
|
||||
res = env->CallStaticByteMethodV(clazz, id, args);
|
||||
else if constexpr (std::is_same_v<T, jchar>)
|
||||
res = env->CallStaticCharMethodV(clazz, id, args);
|
||||
else if constexpr (std::is_same_v<T, jshort>)
|
||||
res = env->CallStaticShortMethodV(clazz, id, args);
|
||||
else if constexpr (std::is_same_v<T, jint>)
|
||||
res = env->CallStaticIntMethodV(clazz, id, args);
|
||||
else if constexpr (std::is_same_v<T, jlong>)
|
||||
res = env->CallStaticLongMethodV(clazz, id, args);
|
||||
else if constexpr (likeIntegerType<T, jbyte>)
|
||||
res = T{env->CallStaticByteMethodV(clazz, id, args)};
|
||||
else if constexpr (likeIntegerType<T, jchar>)
|
||||
res = T{env->CallStaticCharMethodV(clazz, id, args)};
|
||||
else if constexpr (likeIntegerType<T, jshort>)
|
||||
res = T{env->CallStaticShortMethodV(clazz, id, args)};
|
||||
else if constexpr (likeIntegerType<T, jint>)
|
||||
res = T{env->CallStaticIntMethodV(clazz, id, args)};
|
||||
else if constexpr (likeIntegerType<T, jlong>)
|
||||
res = T{env->CallStaticLongMethodV(clazz, id, args)};
|
||||
else if constexpr (std::is_same_v<T, jfloat>)
|
||||
res = env->CallStaticFloatMethodV(clazz, id, args);
|
||||
else if constexpr (std::is_same_v<T, jdouble>)
|
||||
@ -605,16 +620,16 @@ private:
|
||||
{
|
||||
if constexpr (std::is_same_v<T, jboolean>)
|
||||
res = env->GetBooleanField(obj, id);
|
||||
else if constexpr (std::is_same_v<T, jbyte>)
|
||||
res = env->GetByteField(obj, id);
|
||||
else if constexpr (std::is_same_v<T, jchar>)
|
||||
res = env->GetCharField(obj, id);
|
||||
else if constexpr (std::is_same_v<T, jshort>)
|
||||
res = env->GetShortField(obj, id);
|
||||
else if constexpr (std::is_same_v<T, jint>)
|
||||
res = env->GetIntField(obj, id);
|
||||
else if constexpr (std::is_same_v<T, jlong>)
|
||||
res = env->GetLongField(obj, id);
|
||||
else if constexpr (likeIntegerType<T, jbyte>)
|
||||
res = T{env->GetByteField(obj, id)};
|
||||
else if constexpr (likeIntegerType<T, jchar>)
|
||||
res = T{env->GetCharField(obj, id)};
|
||||
else if constexpr (likeIntegerType<T, jshort>)
|
||||
res = T{env->GetShortField(obj, id)};
|
||||
else if constexpr (likeIntegerType<T, jint>)
|
||||
res = T{env->GetIntField(obj, id)};
|
||||
else if constexpr (likeIntegerType<T, jlong>)
|
||||
res = T{env->GetLongField(obj, id)};
|
||||
else if constexpr (std::is_same_v<T, jfloat>)
|
||||
res = env->GetFloatField(obj, id);
|
||||
else if constexpr (std::is_same_v<T, jdouble>)
|
||||
@ -629,16 +644,16 @@ private:
|
||||
{
|
||||
if constexpr (std::is_same_v<T, jboolean>)
|
||||
res = env->GetStaticBooleanField(clazz, id);
|
||||
else if constexpr (std::is_same_v<T, jbyte>)
|
||||
res = env->GetStaticByteField(clazz, id);
|
||||
else if constexpr (std::is_same_v<T, jchar>)
|
||||
res = env->GetStaticCharField(clazz, id);
|
||||
else if constexpr (std::is_same_v<T, jshort>)
|
||||
res = env->GetStaticShortField(clazz, id);
|
||||
else if constexpr (std::is_same_v<T, jint>)
|
||||
res = env->GetStaticIntField(clazz, id);
|
||||
else if constexpr (std::is_same_v<T, jlong>)
|
||||
res = env->GetStaticLongField(clazz, id);
|
||||
else if constexpr (likeIntegerType<T, jbyte>)
|
||||
res = T{env->GetStaticByteField(clazz, id)};
|
||||
else if constexpr (likeIntegerType<T, jchar>)
|
||||
res = T{env->GetStaticCharField(clazz, id)};
|
||||
else if constexpr (likeIntegerType<T, jshort>)
|
||||
res = T{env->GetStaticShortField(clazz, id)};
|
||||
else if constexpr (likeIntegerType<T, jint>)
|
||||
res = T{env->GetStaticIntField(clazz, id)};
|
||||
else if constexpr (likeIntegerType<T, jlong>)
|
||||
res = T{env->GetStaticLongField(clazz, id)};
|
||||
else if constexpr (std::is_same_v<T, jfloat>)
|
||||
res = env->GetStaticFloatField(clazz, id);
|
||||
else if constexpr (std::is_same_v<T, jdouble>)
|
||||
@ -653,16 +668,16 @@ private:
|
||||
{
|
||||
if constexpr (std::is_same_v<T, jboolean>)
|
||||
env->SetBooleanField(obj, id, value);
|
||||
else if constexpr (std::is_same_v<T, jbyte>)
|
||||
env->SetByteField(obj, id, value);
|
||||
else if constexpr (std::is_same_v<T, jchar>)
|
||||
env->SetCharField(obj, id, value);
|
||||
else if constexpr (std::is_same_v<T, jshort>)
|
||||
env->SetShortField(obj, id, value);
|
||||
else if constexpr (std::is_same_v<T, jint>)
|
||||
env->SetIntField(obj, id, value);
|
||||
else if constexpr (std::is_same_v<T, jlong>)
|
||||
env->SetLongField(obj, id, value);
|
||||
else if constexpr (likeIntegerType<T, jbyte>)
|
||||
env->SetByteField(obj, id, static_cast<jbyte>(value));
|
||||
else if constexpr (likeIntegerType<T, jchar>)
|
||||
env->SetCharField(obj, id, static_cast<jchar>(value));
|
||||
else if constexpr (likeIntegerType<T, jshort>)
|
||||
env->SetShortField(obj, id, static_cast<jshort>(value));
|
||||
else if constexpr (likeIntegerType<T, jint>)
|
||||
env->SetIntField(obj, id, static_cast<jint>(value));
|
||||
else if constexpr (likeIntegerType<T, jlong>)
|
||||
env->SetLongField(obj, id, static_cast<jlong>(value));
|
||||
else if constexpr (std::is_same_v<T, jfloat>)
|
||||
env->SetFloatField(obj, id, value);
|
||||
else if constexpr (std::is_same_v<T, jdouble>)
|
||||
@ -679,16 +694,16 @@ private:
|
||||
{
|
||||
if constexpr (std::is_same_v<T, jboolean>)
|
||||
env->SetStaticBooleanField(clazz, id, value);
|
||||
else if constexpr (std::is_same_v<T, jbyte>)
|
||||
env->SetStaticByteField(clazz, id, value);
|
||||
else if constexpr (std::is_same_v<T, jchar>)
|
||||
env->SetStaticCharField(clazz, id, value);
|
||||
else if constexpr (std::is_same_v<T, jshort>)
|
||||
env->SetStaticShortField(clazz, id, value);
|
||||
else if constexpr (std::is_same_v<T, jint>)
|
||||
env->SetStaticIntField(clazz, id, value);
|
||||
else if constexpr (std::is_same_v<T, jlong>)
|
||||
env->SetStaticLongField(clazz, id, value);
|
||||
else if constexpr (likeIntegerType<T, jbyte>)
|
||||
env->SetStaticByteField(clazz, id, static_cast<jbyte>(value));
|
||||
else if constexpr (likeIntegerType<T, jchar>)
|
||||
env->SetStaticCharField(clazz, id, static_cast<jchar>(value));
|
||||
else if constexpr (likeIntegerType<T, jshort>)
|
||||
env->SetStaticShortField(clazz, id, static_cast<jshort>(value));
|
||||
else if constexpr (likeIntegerType<T, jint>)
|
||||
env->SetStaticIntField(clazz, id, static_cast<jint>(value));
|
||||
else if constexpr (likeIntegerType<T, jlong>)
|
||||
env->SetStaticLongField(clazz, id, static_cast<jlong>(value));
|
||||
else if constexpr (std::is_same_v<T, jfloat>)
|
||||
env->SetStaticFloatField(clazz, id, value);
|
||||
else if constexpr (std::is_same_v<T, jdouble>)
|
||||
|
@ -237,6 +237,8 @@ struct Traits {
|
||||
return CTString("D");
|
||||
} else if constexpr (std::is_same_v<T, void>) {
|
||||
return CTString("V");
|
||||
} else if constexpr (std::is_enum_v<T>) {
|
||||
return Traits<std::underlying_type_t<T>>::signature();
|
||||
}
|
||||
// else: return void -> not implemented
|
||||
}
|
||||
|
@ -847,6 +847,10 @@ void tst_QJniObject::getStaticIntField()
|
||||
|
||||
jint i = QJniObject::getStaticField<jint>(cls, "SIZE");
|
||||
QCOMPARE(i, 64);
|
||||
|
||||
enum class Enum { SIZE = 64 };
|
||||
Enum e = QJniObject::getStaticField<Enum>(cls, "SIZE");
|
||||
QCOMPARE(e, Enum::SIZE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::getStaticByteFieldClassName()
|
||||
@ -863,6 +867,10 @@ void tst_QJniObject::getStaticByteField()
|
||||
|
||||
jbyte i = QJniObject::getStaticField<jbyte>(cls, "MAX_VALUE");
|
||||
QCOMPARE(i, jbyte(127));
|
||||
|
||||
enum class Enum : jbyte { MAX_VALUE = 127 };
|
||||
Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE");
|
||||
QCOMPARE(e, Enum::MAX_VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::getStaticLongFieldClassName()
|
||||
@ -879,6 +887,10 @@ void tst_QJniObject::getStaticLongField()
|
||||
|
||||
jlong i = QJniObject::getStaticField<jlong>(cls, "MAX_VALUE");
|
||||
QCOMPARE(i, jlong(9223372036854775807L));
|
||||
|
||||
enum class Enum : jlong { MAX_VALUE = 9223372036854775807L };
|
||||
Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE");
|
||||
QCOMPARE(e, Enum::MAX_VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::getStaticDoubleFieldClassName()
|
||||
@ -931,6 +943,9 @@ void tst_QJniObject::getStaticShortField()
|
||||
|
||||
jshort i = QJniObject::getStaticField<jshort>(cls, "MAX_VALUE");
|
||||
QCOMPARE(i, jshort(32767));
|
||||
enum class Enum : jshort { MAX_VALUE = 32767 };
|
||||
Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE");
|
||||
QCOMPARE(e, Enum::MAX_VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::getStaticCharFieldClassName()
|
||||
@ -947,6 +962,10 @@ void tst_QJniObject::getStaticCharField()
|
||||
|
||||
jchar i = QJniObject::getStaticField<jchar>(cls, "MAX_VALUE");
|
||||
QCOMPARE(i, jchar(0xffff));
|
||||
|
||||
enum class Enum : jchar { MAX_VALUE = 0xffff };
|
||||
Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE");
|
||||
QCOMPARE(e, Enum::MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
@ -982,16 +1001,22 @@ void setField(const char *fieldName, T testValue)
|
||||
void tst_QJniObject::setIntField()
|
||||
{
|
||||
setField("INT_VAR", 555);
|
||||
enum class Enum : jint { VALUE = 555 };
|
||||
setField("INT_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setByteField()
|
||||
{
|
||||
setField("BYTE_VAR", jbyte(555));
|
||||
setField("BYTE_VAR", jbyte(123));
|
||||
enum class Enum : jbyte { VALUE = 123 };
|
||||
setField("BYTE_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setLongField()
|
||||
{
|
||||
setField("LONG_VAR", jlong(9223372036847758232L));
|
||||
enum class Enum : jlong { VALUE = 9223372036847758232L };
|
||||
setField("LONG_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setDoubleField()
|
||||
@ -1006,12 +1031,16 @@ void tst_QJniObject::setFloatField()
|
||||
|
||||
void tst_QJniObject::setShortField()
|
||||
{
|
||||
setField("SHORT_VAR", jshort(123));
|
||||
setField("SHORT_VAR", jshort(555));
|
||||
enum class Enum : jshort { VALUE = 555 };
|
||||
setField("SHORT_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setCharField()
|
||||
{
|
||||
setField("CHAR_VAR", jchar('A'));
|
||||
enum class Enum : jchar { VALUE = 'A' };
|
||||
setField("CHAR_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setBooleanField()
|
||||
@ -1049,16 +1078,22 @@ void setStaticField(const char *fieldName, T testValue)
|
||||
void tst_QJniObject::setStaticIntField()
|
||||
{
|
||||
setStaticField("S_INT_VAR", 555);
|
||||
enum class Enum : jint { VALUE = 555 };
|
||||
setStaticField("S_INT_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setStaticByteField()
|
||||
{
|
||||
setStaticField("S_BYTE_VAR", jbyte(555));
|
||||
setStaticField("S_BYTE_VAR", jbyte(123));
|
||||
enum class Enum : jbyte { VALUE = 123 };
|
||||
setStaticField("S_BYTE_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setStaticLongField()
|
||||
{
|
||||
setStaticField("S_LONG_VAR", jlong(9223372036847758232L));
|
||||
enum class Enum : jlong { VALUE = 9223372036847758232L };
|
||||
setStaticField("S_LONG_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setStaticDoubleField()
|
||||
@ -1073,12 +1108,16 @@ void tst_QJniObject::setStaticFloatField()
|
||||
|
||||
void tst_QJniObject::setStaticShortField()
|
||||
{
|
||||
setStaticField("S_SHORT_VAR", jshort(123));
|
||||
setStaticField("S_SHORT_VAR", jshort(555));
|
||||
enum class Enum : jshort { VALUE = 555 };
|
||||
setStaticField("S_SHORT_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setStaticCharField()
|
||||
{
|
||||
setStaticField("S_CHAR_VAR", jchar('A'));
|
||||
enum class Enum : jchar { VALUE = 'A' };
|
||||
setStaticField("S_CHAR_VAR", Enum::VALUE);
|
||||
}
|
||||
|
||||
void tst_QJniObject::setStaticBooleanField()
|
||||
|
@ -115,6 +115,23 @@ static_assert(!QtJniTypes::CTString("ABCDE").endsWith("ABCDEF"));
|
||||
static_assert(QtJniTypes::CTString("ABCDE").endsWith('E'));
|
||||
static_assert(!QtJniTypes::CTString("ABCDE").endsWith('F'));
|
||||
|
||||
enum UnscopedEnum {};
|
||||
enum class ScopedEnum {};
|
||||
enum class IntEnum : int {};
|
||||
enum class UnsignedEnum : unsigned {};
|
||||
enum class Int8Enum : int8_t {};
|
||||
enum class ShortEnum : short {};
|
||||
enum class LongEnum : long {};
|
||||
enum class JIntEnum : jint {};
|
||||
|
||||
static_assert(QtJniTypes::Traits<UnscopedEnum>::signature() == "I");
|
||||
static_assert(QtJniTypes::Traits<ScopedEnum>::signature() == "I");
|
||||
static_assert(QtJniTypes::Traits<IntEnum>::signature() == "I");
|
||||
static_assert(QtJniTypes::Traits<UnsignedEnum>::signature() == "I");
|
||||
static_assert(QtJniTypes::Traits<Int8Enum>::signature() == "B");
|
||||
static_assert(QtJniTypes::Traits<LongEnum>::signature() == "J");
|
||||
static_assert(QtJniTypes::Traits<JIntEnum>::signature() == "I");
|
||||
|
||||
void tst_QJniTypes::initTestCase()
|
||||
{
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user