Metersnap JNI Wrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef METERSNAP_ANDROID_JNICLASS_H | |
#define METERSNAP_ANDROID_JNICLASS_H | |
#include <jni.h> | |
#include <string> | |
#include <sstream> | |
class JniClass; | |
class JniObject; | |
namespace impl{ | |
template <typename T> | |
std::string GetTypeName(); | |
typedef unsigned char jboolean; /* unsigned 8 bits */ | |
typedef signed char jbyte; /* signed 8 bits */ | |
typedef unsigned short jchar; /* unsigned 16 bits */ | |
typedef short jshort; /* signed 16 bits */ | |
typedef int jint; /* signed 32 bits */ | |
typedef long long jlong; /* signed 64 bits */ | |
typedef float jfloat; /* 32-bit IEEE 754 */ | |
typedef double jdouble; /* 64-bit IEEE 754 */ | |
#define REGISTER_TYPE(type, name) template<> std::string GetTypeName<type>() { return name; } | |
REGISTER_TYPE(jboolean, "Z"); | |
REGISTER_TYPE(jbyte , "B"); | |
REGISTER_TYPE(jchar , "C"); | |
REGISTER_TYPE(jshort , "S"); | |
REGISTER_TYPE(jint , "I"); | |
REGISTER_TYPE(jlong , "J"); | |
REGISTER_TYPE(jfloat , "F"); | |
REGISTER_TYPE(jdouble , "D"); | |
REGISTER_TYPE(void , "V"); | |
REGISTER_TYPE(const char *, "Ljava/lang/String;"); | |
#undef REGISTER_TYPE | |
class JniHolder{ | |
jvalue m_val; | |
class Holder{ | |
JNIEnv *m_env; | |
jobject m_obj; | |
public: | |
Holder(JNIEnv *env = nullptr, jobject obj = nullptr): | |
m_env(env), m_obj(obj){ | |
} | |
~Holder(){ | |
} | |
jobject obj() const { | |
return m_obj; | |
} | |
} m_holder; | |
public: | |
#define CONSTRUCTOR(type, field) explicit JniHolder(JNIEnv *env, type val) : m_val(), m_holder() { field = val; } | |
CONSTRUCTOR(jboolean, m_val.z); | |
CONSTRUCTOR(jbyte , m_val.b); | |
CONSTRUCTOR(jchar , m_val.c); | |
CONSTRUCTOR(jshort , m_val.s); | |
CONSTRUCTOR(jint , m_val.i); | |
CONSTRUCTOR(jlong , m_val.j); | |
CONSTRUCTOR(jfloat , m_val.f); | |
CONSTRUCTOR(jdouble , m_val.d); | |
#undef CONSTRUCTOR | |
explicit JniHolder(JNIEnv *env, const std::string &val) : m_val(), m_holder(env, env->NewStringUTF(val.c_str())) { m_val.l = m_holder.obj(); } | |
explicit JniHolder(JNIEnv *env, jobject obj) : m_val(), m_holder(env, obj) { m_val.l = m_holder.obj(); } | |
const jvalue &value() const { | |
return m_val; | |
} | |
}; | |
template <typename Ret> | |
struct CallStaticMethodImp; | |
template <typename Ret> | |
struct CallInstanceMethodImp; | |
#define STATIC_METHOD_IMP(type, _jname) \ | |
template <>\ | |
struct CallStaticMethodImp<type>{\ | |
static type CallMethod(JNIEnv *env, jclass clazz, jmethodID method, size_t args_size, jvalue *values){\ | |
if(args_size != 0)\ | |
return env->CallStatic##_jname##MethodA(clazz, method, values);\ | |
return env->CallStatic##_jname##Method(clazz, method);\ | |
}\ | |
};\ | |
template <>\ | |
struct CallInstanceMethodImp<type>{\ | |
static type CallMethod(JNIEnv *env, jobject object, jmethodID method, size_t args_size, jvalue *values){\ | |
if(args_size != 0)\ | |
return env->Call##_jname##MethodA(object, method, values);\ | |
return env->Call##_jname##Method(object, method);\ | |
}\ | |
}; | |
STATIC_METHOD_IMP(jboolean, Boolean); | |
STATIC_METHOD_IMP(jbyte , Byte); | |
STATIC_METHOD_IMP(jchar , Char); | |
STATIC_METHOD_IMP(jshort , Short); | |
STATIC_METHOD_IMP(jint , Int); | |
STATIC_METHOD_IMP(jlong , Long); | |
STATIC_METHOD_IMP(jfloat , Float); | |
STATIC_METHOD_IMP(jdouble , Double); | |
STATIC_METHOD_IMP(void , Void); | |
STATIC_METHOD_IMP(jobject , Object); | |
#undef STATIC_METHOD_IMP | |
template <typename... Args> | |
std::string GenerateTypesString(){ | |
constexpr size_t count = sizeof...(Args); | |
const std::string typeNames[count] = { GetTypeName<Args>()... }; | |
std::string signature; | |
signature.append("("); | |
for(const std::string &s : typeNames) | |
signature.append(s); | |
signature.append(")"); | |
return signature; | |
}; | |
template <typename... Args> | |
jmethodID GetConstructorId(JNIEnv *env, jclass clazz){ | |
std::string signature = GenerateTypesString<Args...>() + "V"; | |
return env->GetMethodID(clazz, "<init>", signature.c_str()); | |
}; | |
template <typename Ret, typename... Args> | |
jmethodID GetMethodId(JNIEnv *env, jclass clazz, const std::string &name){ | |
std::string signature = GenerateTypesString<Args...>() + GetTypeName<Ret>(); | |
return env->GetMethodID(clazz, name.c_str(), signature.c_str()); | |
}; | |
template <typename Ret> | |
struct CallStatic { | |
template<typename... Args> | |
static Ret CallStaticMethod(JniClass *clazz, const std::string &name, Args... args); | |
}; | |
template <> | |
struct CallStatic<jobject> { | |
template<typename... Args> | |
static jobject CallStaticMethod(JniClass *clazz, const std::string &name, const JniClass *returnType, Args... args); | |
template<typename... Args> | |
static jobject CallStaticMethod(JniClass *clazz, const std::string &name, const JniClass &returnType, Args... args); | |
}; | |
template <> | |
struct CallStatic<JniObject> { | |
template<typename... Args> | |
static JniObject CallStaticMethod(JniClass *clazz, const std::string &name, const JniClass *returnType, Args... args); | |
template<typename... Args> | |
static JniObject CallStaticMethod(JniClass *clazz, const std::string &name, const JniClass &returnType, Args... args); | |
}; | |
template <typename Ret> | |
struct CallInstance{ | |
template <typename... Args> | |
static Ret CallInstanceMethod(JniObject *obj, const std::string &name, Args... args); | |
}; | |
template <typename Ret> | |
struct CallInstanceSignature{ | |
template<typename... Args> | |
static Ret CallInstanceSignatureMethod(JniObject *obj, const std::string &name, const std::string &signature, Args... args); | |
}; | |
} | |
template<typename T> | |
std::string to_string(T value) { | |
std::ostringstream os; | |
os << value; | |
return os.str(); | |
} | |
class JniObject { | |
public: | |
JniObject(const JniClass *clazz = nullptr, jobject obj = nullptr): | |
m_object(obj), | |
m_class(clazz){ | |
} | |
bool isValid() const{ | |
return m_object != nullptr; | |
} | |
jobject object() const { | |
return m_object; | |
} | |
const JniClass *clazz() const { | |
return m_class; | |
} | |
template <typename Ret, typename... Args> | |
Ret invoke(const std::string &method, Args... args){ | |
return impl::CallInstance<Ret>::CallInstanceMethod(this, method, std::move(args)...); | |
}; | |
template <typename Ret, typename... Args> | |
Ret invokeSignature(const std::string &method, const std::string &signature, Args... args){ | |
return impl::CallInstanceSignature<Ret>::CallInstanceSignatureMethod(this, method, signature, std::move(args)...); | |
}; | |
private: | |
jobject m_object; | |
const JniClass * m_class; | |
}; | |
class JniClass { | |
public: | |
JniClass(JNIEnv *env, const std::string &name): | |
m_env(env), | |
m_name(name), | |
m_clazz(env->FindClass(name.c_str())) { | |
} | |
template <typename Ret, typename... Args> | |
Ret invokeStatic(const std::string &method, Args... args){ | |
return impl::CallStatic<Ret>::CallStaticMethod(this, method.c_str(), std::move(args)...); | |
}; | |
template <typename... Args> | |
JniObject newInstance(Args... args){ | |
jmethodID constructor = impl::GetConstructorId<Args...>(m_env, m_clazz); | |
if(constructor != 0) { | |
constexpr size_t arg_count = sizeof...(Args); | |
impl::JniHolder holder[arg_count] = { std::move(impl::JniHolder(m_env, args))... }; | |
jvalue values[arg_count] {}; | |
for(int i = 0; i < arg_count; i++){ | |
values[i] = holder[i].value(); | |
} | |
return { this, m_env->NewObjectA(m_clazz, constructor, values) }; | |
} | |
return {}; | |
} | |
JNIEnv *env() const { | |
return m_env; | |
} | |
jclass clazz() const { | |
return m_clazz; | |
} | |
const std::string &name() const { | |
return m_name; | |
} | |
private: | |
JNIEnv *m_env; | |
std::string m_name; | |
jclass m_clazz; | |
}; | |
namespace impl{ | |
template<typename Ret> | |
template<typename... Args> | |
Ret CallStatic<Ret>::CallStaticMethod(JniClass *clazz, const std::string &name, Args... args) { | |
constexpr size_t arg_count = sizeof...(Args); | |
JniHolder holder[arg_count] = {std::move(JniHolder(clazz->env(), args))...}; | |
jvalue values[arg_count]{}; | |
for (int i = 0; i < arg_count; i++) { | |
values[i] = holder[i].value(); | |
} | |
std::string signature = GenerateTypesString<Args...>() + GetTypeName<Ret>(); | |
jmethodID method = clazz->env()->GetStaticMethodID(clazz->clazz(), name.c_str(), | |
signature.c_str()); | |
return CallStaticMethodImp<Ret>::CallMethod(clazz->env(), clazz->clazz(), method, arg_count, values); | |
}; | |
template<> | |
template<typename... Args> | |
jobject CallStatic<jobject>::CallStaticMethod(JniClass *clazz, const std::string &name, const JniClass *returnType, Args... args) { | |
constexpr size_t arg_count = sizeof...(Args); | |
JniHolder holder[arg_count] = {std::move(JniHolder(clazz->env(), args))...}; | |
jvalue values[arg_count]{}; | |
for (int i = 0; i < arg_count; i++) { | |
values[i] = holder[i].value(); | |
} | |
std::string signature = GenerateTypesString<Args...>() + "L" + returnType->name() + ";"; | |
jmethodID method = clazz->env()->GetStaticMethodID(clazz->clazz(), name.c_str(), | |
signature.c_str()); | |
return CallStaticMethodImp<jobject>::CallMethod(clazz->env(), clazz->clazz(), method, arg_count, values); | |
}; | |
template<> | |
template<typename... Args> | |
jobject CallStatic<jobject>::CallStaticMethod(JniClass *clazz, const std::string &name, const JniClass &returnType, Args... args) { | |
return CallStaticMethod(clazz, name, &returnType, std::move(args)...); | |
}; | |
template<> | |
template<typename... Args> | |
JniObject CallStatic<JniObject>::CallStaticMethod(JniClass *clazz, const std::string &name, const JniClass *returnType, Args... args) { | |
constexpr size_t arg_count = sizeof...(Args); | |
JniHolder holder[arg_count] = {std::move(JniHolder(clazz->env(), args))...}; | |
jvalue values[arg_count]{}; | |
for (int i = 0; i < arg_count; i++) { | |
values[i] = holder[i].value(); | |
} | |
std::string signature = GenerateTypesString<Args...>() + "L" + returnType->name() + ";"; | |
jmethodID method = clazz->env()->GetStaticMethodID(clazz->clazz(), name.c_str(), signature.c_str()); | |
return { returnType, CallStaticMethodImp<jobject>::CallMethod(clazz->env(), clazz->clazz(), method, arg_count, values) }; | |
}; | |
template<> | |
template<typename... Args> | |
JniObject CallStatic<JniObject>::CallStaticMethod(JniClass *clazz, const std::string &name, const JniClass &returnType, Args... args) { | |
return CallStaticMethod(clazz, name, &returnType, std::move(args)...); | |
}; | |
template <typename Ret> | |
template <typename... Args> | |
Ret CallInstance<Ret>::CallInstanceMethod(JniObject *obj, const std::string &name, Args... args){ | |
jmethodID method = GetMethodId<Ret, Args...>(obj->clazz()->env(), obj->clazz()->clazz(), name); | |
constexpr size_t arg_count = sizeof...(Args); | |
JniHolder holder[arg_count] = {std::move(JniHolder(obj->clazz()->env(), args))...}; | |
jvalue values[arg_count]{}; | |
for (int i = 0; i < arg_count; i++) { | |
values[i] = holder[i].value(); | |
} | |
return CallInstanceMethodImp<Ret>::CallMethod(obj->clazz()->env(), obj->object(), method, arg_count, values); | |
}; | |
template <typename Ret> | |
template <typename... Args> | |
Ret CallInstanceSignature<Ret>::CallInstanceSignatureMethod(JniObject *obj, const std::string &name, const std::string &signature, Args... args){ | |
jmethodID method = obj->clazz()->env()->GetMethodID(obj->clazz()->clazz(), name.c_str(), signature.c_str()); | |
constexpr size_t arg_count = sizeof...(Args); | |
JniHolder holder[arg_count] = {std::move(JniHolder(obj->clazz()->env(), args))...}; | |
jvalue values[arg_count]{}; | |
for (int i = 0; i < arg_count; i++) { | |
values[i] = holder[i].value(); | |
} | |
return CallInstanceMethodImp<Ret>::CallMethod(obj->clazz()->env(), obj->object(), method, arg_count, values); | |
}; | |
} | |
#endif //METERSNAP_ANDROID_JNICLASS_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment