mirror of
https://github.com/bab2min/Kiwi.git
synced 2026-06-17 01:54:27 +00:00
1714 lines
43 KiB
C++
1714 lines
43 KiB
C++
#pragma once
|
|
#include <array>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <tuple>
|
|
#include <vector>
|
|
#include <optional>
|
|
#include <iostream>
|
|
|
|
#include <jni.h>
|
|
|
|
namespace jni
|
|
{
|
|
using namespace std::literals;
|
|
|
|
struct StringConcat {
|
|
template <std::string_view const&... Vs>
|
|
struct Helper {
|
|
static constexpr auto Build() noexcept {
|
|
constexpr std::size_t len = (Vs.size() + ... + 0);
|
|
std::array<char, len + 1> arr{};
|
|
auto n =
|
|
[i = 0, &arr](auto const& s) mutable {
|
|
for (auto c : s) arr[i++] = c;
|
|
};
|
|
(n(Vs), ...);
|
|
arr[len] = 0;
|
|
|
|
return arr;
|
|
}
|
|
|
|
static constexpr auto arr = Build();
|
|
static constexpr std::string_view value{ arr.data(), arr.size() - 1 };
|
|
};
|
|
|
|
template <std::string_view const&... Vs>
|
|
static constexpr std::string_view value = Helper<Vs...>::value;
|
|
};
|
|
|
|
template <std::string_view const&... Vs>
|
|
static constexpr auto StringConcat_v = StringConcat::value<Vs...>;
|
|
|
|
static constexpr auto svLParen = "("sv;
|
|
static constexpr auto svRParen = ")"sv;
|
|
static constexpr auto svLBrack = "["sv;
|
|
static constexpr auto svV = "V"sv;
|
|
static constexpr auto svL = "L"sv;
|
|
static constexpr auto svSC = ";"sv;
|
|
static constexpr auto svNullTerm = "\x00"sv;
|
|
static constexpr auto svNotInstanceOf = "Object isn't instance of "sv;
|
|
|
|
template<class Func>
|
|
inline auto handleExc(JNIEnv* env, Func&& func);
|
|
|
|
template<class Ty>
|
|
struct NativeMethod
|
|
{
|
|
const char* name;
|
|
const char* signature;
|
|
Ty fnPtr;
|
|
|
|
operator JNINativeMethod() const
|
|
{
|
|
return { (char*)name, (char*)signature, (void*)fnPtr };
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
class JObject
|
|
{
|
|
public:
|
|
static jclass jClass;
|
|
static jfieldID jInstField;
|
|
static jmethodID jInitMethod;
|
|
};
|
|
|
|
template<class Ty>
|
|
jclass JObject<Ty>::jClass = nullptr;
|
|
|
|
template<class Ty>
|
|
jfieldID JObject<Ty>::jInstField = nullptr;
|
|
|
|
template<class Ty>
|
|
jmethodID JObject<Ty>::jInitMethod = nullptr;
|
|
|
|
template<class Ty>
|
|
class JRef
|
|
{
|
|
protected:
|
|
JNIEnv* env;
|
|
jobject inst;
|
|
public:
|
|
JRef(JNIEnv* _env = nullptr, jobject _inst = nullptr)
|
|
: env{ _env }, inst{ _inst }
|
|
{}
|
|
|
|
JRef(const JRef&) = default;
|
|
JRef(JRef&&) = default;
|
|
|
|
Ty& get();
|
|
const Ty& get() const;
|
|
|
|
bool empty() const { return !inst; }
|
|
operator bool() const { return !!inst; }
|
|
operator jobject() const { return inst; }
|
|
|
|
Ty* operator->()
|
|
{
|
|
return &get();
|
|
}
|
|
|
|
const Ty* operator->() const
|
|
{
|
|
return &get();
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
class JUniqueGlobalRef : public JRef<Ty>
|
|
{
|
|
public:
|
|
JUniqueGlobalRef()
|
|
{}
|
|
|
|
JUniqueGlobalRef(JRef<Ty> ref)
|
|
: JRef<Ty>{ ref }
|
|
{
|
|
this->inst = this->env->NewGlobalRef(this->inst);
|
|
}
|
|
|
|
JUniqueGlobalRef(const JUniqueGlobalRef&) = delete;
|
|
JUniqueGlobalRef(JUniqueGlobalRef&& o)
|
|
{
|
|
std::swap(this->env, o.env);
|
|
std::swap(this->inst, o.inst);
|
|
}
|
|
|
|
JUniqueGlobalRef& operator=(const JUniqueGlobalRef&) = delete;
|
|
JUniqueGlobalRef& operator=(JUniqueGlobalRef&& o)
|
|
{
|
|
std::swap(this->env, o.env);
|
|
std::swap(this->inst, o.inst);
|
|
return *this;
|
|
}
|
|
|
|
~JUniqueGlobalRef()
|
|
{
|
|
if (this->env && this->inst)
|
|
{
|
|
this->env->DeleteGlobalRef(this->inst);
|
|
this->env = nullptr;
|
|
this->inst = nullptr;
|
|
}
|
|
}
|
|
};
|
|
|
|
class JIteratorBase : public JUniqueGlobalRef<int>
|
|
{
|
|
public:
|
|
static inline jclass jClass;
|
|
static inline jmethodID jHasNext;
|
|
static inline jmethodID jNext;
|
|
|
|
JIteratorBase(JNIEnv* _env, jobject _inst)
|
|
: JUniqueGlobalRef{ JRef{ _env, _inst } }
|
|
{}
|
|
};
|
|
|
|
template<class Ty>
|
|
class JIterator : public JIteratorBase
|
|
{
|
|
public:
|
|
using JIteratorBase::JIteratorBase;
|
|
|
|
bool hasNext();
|
|
Ty next();
|
|
|
|
int& get() = delete;
|
|
const int& get() const = delete;
|
|
int* operator->() = delete;
|
|
const int* operator->() const = delete;
|
|
};
|
|
|
|
template<class Ty>
|
|
struct JClassName
|
|
{
|
|
static constexpr auto value = Ty::className;
|
|
};
|
|
|
|
template<class Ty>
|
|
static constexpr auto jclassName = JClassName<Ty>::value;
|
|
|
|
template<class Ty, class = void>
|
|
struct ValueBuilder;
|
|
|
|
template<class Ty>
|
|
using ToJniType = typename ValueBuilder<Ty>::JniType;
|
|
|
|
template<class Ty>
|
|
static constexpr auto toJniTypeStr = ValueBuilder<Ty>::typeStr;
|
|
|
|
template<>
|
|
struct ValueBuilder<void>
|
|
{
|
|
using CppType = void;
|
|
using JniType = void;
|
|
static constexpr auto typeStr = "V"sv;
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<uint8_t>
|
|
{
|
|
using CppType = uint8_t;
|
|
using JniType = jbyte;
|
|
static constexpr auto typeStr = "B"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<int8_t>
|
|
{
|
|
using CppType = int8_t;
|
|
using JniType = jbyte;
|
|
static constexpr auto typeStr = "B"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<uint16_t>
|
|
{
|
|
using CppType = uint16_t;
|
|
using JniType = jshort;
|
|
static constexpr auto typeStr = "S"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<int16_t>
|
|
{
|
|
using CppType = int16_t;
|
|
using JniType = jshort;
|
|
static constexpr auto typeStr = "S"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<uint32_t>
|
|
{
|
|
using CppType = uint32_t;
|
|
using JniType = jint;
|
|
static constexpr auto typeStr = "I"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<int32_t>
|
|
{
|
|
using CppType = int32_t;
|
|
using JniType = jint;
|
|
static constexpr auto typeStr = "I"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
#ifdef _JNI_INT64_TO_INT
|
|
template<>
|
|
struct ValueBuilder<uint64_t>
|
|
{
|
|
using CppType = uint64_t;
|
|
using JniType = jint;
|
|
static constexpr auto typeStr = "I"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<int64_t>
|
|
{
|
|
using CppType = int64_t;
|
|
using JniType = jint;
|
|
static constexpr auto typeStr = "I"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
#ifdef __APPLE__
|
|
template<>
|
|
struct ValueBuilder<unsigned long>
|
|
{
|
|
using CppType = unsigned long;
|
|
using JniType = jint;
|
|
static constexpr auto typeStr = "I"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<long>
|
|
{
|
|
using CppType = long;
|
|
using JniType = jint;
|
|
static constexpr auto typeStr = "I"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
#endif
|
|
#else
|
|
template<>
|
|
struct ValueBuilder<uint64_t>
|
|
{
|
|
using CppType = uint64_t;
|
|
using JniType = jlong;
|
|
static constexpr auto typeStr = "J"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<int64_t>
|
|
{
|
|
using CppType = int64_t;
|
|
using JniType = jlong;
|
|
static constexpr auto typeStr = "J"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
#ifdef __APPLE__
|
|
template<>
|
|
struct ValueBuilder<unsigned long>
|
|
{
|
|
using CppType = unsigned long;
|
|
using JniType = jlong;
|
|
static constexpr auto typeStr = "J"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<long>
|
|
{
|
|
using CppType = long;
|
|
using JniType = jlong;
|
|
static constexpr auto typeStr = "J"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
template<>
|
|
struct ValueBuilder<bool>
|
|
{
|
|
using CppType = bool;
|
|
using JniType = jboolean;
|
|
static constexpr auto typeStr = "Z"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return !!v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v ? -1 : 0;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<char16_t>
|
|
{
|
|
using CppType = char16_t;
|
|
using JniType = jchar;
|
|
static constexpr auto typeStr = "C"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<float>
|
|
{
|
|
using CppType = float;
|
|
using JniType = jfloat;
|
|
static constexpr auto typeStr = "F"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<double>
|
|
{
|
|
using CppType = double;
|
|
using JniType = jdouble;
|
|
static constexpr auto typeStr = "D"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType v)
|
|
{
|
|
return v;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<std::string>
|
|
{
|
|
using CppType = std::string;
|
|
using JniType = jstring;
|
|
static constexpr auto typeStr = "Ljava/lang/String;"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v)
|
|
{
|
|
throw std::bad_optional_access{};
|
|
}
|
|
auto* c = env->GetStringUTFChars(v, nullptr);
|
|
auto size = env->GetStringUTFLength(v);
|
|
return std::string{ c, c + size };
|
|
}
|
|
|
|
CppType fromJava(JNIEnv* env, jobject v)
|
|
{
|
|
return fromJava(env, (jstring)v);
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, const CppType& v)
|
|
{
|
|
return env->NewStringUTF(v.c_str());
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<std::u16string>
|
|
{
|
|
using CppType = std::u16string;
|
|
using JniType = jstring;
|
|
static constexpr auto typeStr = "Ljava/lang/String;"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v)
|
|
{
|
|
throw std::bad_optional_access{};
|
|
}
|
|
auto* c = env->GetStringChars(v, nullptr);
|
|
auto size = env->GetStringLength(v);
|
|
return std::u16string{ (const char16_t*)c, (const char16_t*)c + size };
|
|
}
|
|
|
|
CppType fromJava(JNIEnv* env, jobject v)
|
|
{
|
|
return fromJava(env, (jstring)v);
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, const CppType& v)
|
|
{
|
|
return env->NewString((const jchar*)v.data(), v.size());
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<Ty, std::enable_if_t<std::is_base_of_v<JObject<Ty>, Ty>>>
|
|
{
|
|
using CppType = Ty;
|
|
using JniType = jobject;
|
|
static constexpr auto typeStr = StringConcat_v<svL, jclassName<Ty>, svSC>;
|
|
|
|
CppType& fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v)
|
|
{
|
|
throw std::bad_optional_access{};
|
|
}
|
|
if (!env->IsInstanceOf(v, JObject<Ty>::jClass)) throw std::runtime_error{ StringConcat_v<svNotInstanceOf, typeStr, svNullTerm>.data()};
|
|
auto ptr = (Ty*)env->GetLongField(v, JObject<Ty>::jInstField);
|
|
if (!ptr) throw std::runtime_error{ "Object is already closed or not initialized." };
|
|
return *ptr;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType&& v)
|
|
{
|
|
auto ptr = new CppType{ std::move(v) };
|
|
if (!ptr) throw std::runtime_error{ std::string{ jclassName<Ty> } + ": failed to prepare c++ object." };
|
|
auto ret = env->NewObject(JObject<Ty>::jClass, JObject<Ty>::jInitMethod, (jlong)ptr);
|
|
if (!ret) throw std::runtime_error{ std::string{ jclassName<Ty> } + ": failed to construct object." };
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<std::optional<Ty>, std::enable_if_t<std::is_base_of_v<JObject<Ty>, Ty>>>
|
|
{
|
|
using CppType = std::optional<Ty>;
|
|
using JniType = jobject;
|
|
static constexpr auto typeStr = StringConcat_v<svL, jclassName<Ty>, svSC>;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v) return {};
|
|
if (!env->IsInstanceOf(v, JObject<Ty>::jClass)) throw std::runtime_error{ StringConcat_v<svNotInstanceOf, typeStr, svNullTerm>.data()};
|
|
auto ptr = (Ty*)env->GetLongField(v, JObject<Ty>::jInstField);
|
|
if (!ptr) throw std::runtime_error{ "Object is already closed or not initialized." };
|
|
return *ptr;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType&& v)
|
|
{
|
|
if (!v) return nullptr;
|
|
auto ptr = new CppType{ std::move(*v) };
|
|
if (!ptr) throw std::runtime_error{ std::string{ jclassName<Ty> } + ": failed to prepare c++ object." };
|
|
auto ret = env->NewObject(JObject<Ty>::jClass, JObject<Ty>::jInitMethod, (jlong)ptr);
|
|
if (!ret) throw std::runtime_error{ std::string{ jclassName<Ty> } + ": failed to construct object." };
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<Ty*, std::enable_if_t<std::is_base_of_v<JObject<Ty>, Ty>>>
|
|
{
|
|
using CppType = Ty*;
|
|
using JniType = jobject;
|
|
static constexpr auto typeStr = StringConcat_v<svL, jclassName<Ty>, svSC>;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v) return nullptr;
|
|
if (!env->IsInstanceOf(v, JObject<Ty>::jClass)) throw std::runtime_error{ StringConcat_v<svNotInstanceOf, typeStr, svNullTerm>.data()};
|
|
auto ptr = (Ty*)env->GetLongField(v, JObject<Ty>::jInstField);
|
|
if (!ptr) throw std::runtime_error{ "Object is already closed or not initialized." };
|
|
return ptr;
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<JRef<Ty>, std::enable_if_t<std::is_base_of_v<JObject<Ty>, Ty>>>
|
|
{
|
|
using CppType = JRef<Ty>;
|
|
using JniType = jobject;
|
|
static constexpr auto typeStr = StringConcat_v<svL, jclassName<Ty>, svSC>;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
return CppType{ env, v };
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, CppType&& v)
|
|
{
|
|
return (jobject)v;
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<std::vector<Ty>, std::enable_if_t<!std::is_integral_v<Ty> && !std::is_floating_point_v<Ty>>>
|
|
{
|
|
using CppType = std::vector<Ty>;
|
|
using JniType = jobjectArray;
|
|
static constexpr auto typeStr = StringConcat_v<svLBrack, svL, jclassName<Ty>, svSC>;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v)
|
|
{
|
|
return {};
|
|
}
|
|
size_t len = env->GetArrayLength(v);
|
|
ValueBuilder<Ty> vb;
|
|
std::vector<Ty> ret;
|
|
ret.reserve(len);
|
|
for (size_t i = 0; i < len; ++i)
|
|
{
|
|
ret.emplace_back(vb.fromJava(env, env->GetObjectArrayElement(v, i)));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, const CppType& v)
|
|
{
|
|
auto arr = env->NewObjectArray(v.size(), JObject<Ty>::jClass, nullptr);
|
|
ValueBuilder<Ty> vb;
|
|
for (size_t i = 0; i < v.size(); ++i)
|
|
{
|
|
env->SetObjectArrayElement(arr, i, vb.toJava(env, v[i]));
|
|
}
|
|
return arr;
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<std::optional<std::vector<Ty>>, std::enable_if_t<!std::is_integral_v<Ty> && !std::is_floating_point_v<Ty>>>
|
|
{
|
|
using CppType = std::optional<std::vector<Ty>>;
|
|
using JniType = jobjectArray;
|
|
static constexpr auto typeStr = StringConcat_v<svLBrack, svL, jclassName<Ty>, svSC>;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v) return {};
|
|
size_t len = env->GetArrayLength(v);
|
|
ValueBuilder<Ty> vb;
|
|
std::vector<Ty> ret;
|
|
ret.reserve(len);
|
|
for (size_t i = 0; i < len; ++i)
|
|
{
|
|
ret.emplace_back(vb.fromJava(env, env->GetObjectArrayElement(v, i)));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, const CppType& v)
|
|
{
|
|
if (!v) return nullptr;
|
|
auto arr = env->NewObjectArray(v->size(), JObject<Ty>::jClass, nullptr);
|
|
ValueBuilder<Ty> vb;
|
|
for (size_t i = 0; i < v->size(); ++i)
|
|
{
|
|
env->SetObjectArrayElement(arr, i, vb.toJava(env, (*v)[i]));
|
|
}
|
|
return arr;
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<std::vector<Ty>, std::enable_if_t<std::is_integral_v<Ty>>>
|
|
{
|
|
using CppType = std::vector<Ty>;
|
|
using JniType = std::conditional_t<sizeof(Ty) == 1, jbyteArray,
|
|
std::conditional_t<sizeof(Ty) == 2, jshortArray,
|
|
std::conditional_t<sizeof(Ty) == 4, jintArray,
|
|
std::conditional_t<sizeof(Ty) == 8, jlongArray, void>
|
|
>
|
|
>
|
|
>;
|
|
static constexpr auto typeStr = sizeof(Ty) == 1 ? "[B"sv :
|
|
sizeof(Ty) == 2 ? "[S"sv :
|
|
sizeof(Ty) == 4 ? "[I"sv :
|
|
sizeof(Ty) == 8 ? "[J"sv : "";
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v)
|
|
{
|
|
throw std::bad_optional_access{};
|
|
}
|
|
size_t len = env->GetArrayLength(v);
|
|
std::vector<Ty> ret(len);
|
|
if constexpr (sizeof(Ty) == 1)
|
|
{
|
|
auto ptr = env->GetByteArrayElements(v, nullptr);
|
|
std::copy(ptr, ptr + len, ret.data());
|
|
}
|
|
else if constexpr (sizeof(Ty) == 2)
|
|
{
|
|
auto ptr = env->GetShortArrayElements(v, nullptr);
|
|
std::copy(ptr, ptr + len, ret.data());
|
|
}
|
|
else if constexpr (sizeof(Ty) == 4)
|
|
{
|
|
auto ptr = env->GetIntArrayElements(v, nullptr);
|
|
std::copy(ptr, ptr + len, ret.data());
|
|
}
|
|
else if constexpr (sizeof(Ty) == 8)
|
|
{
|
|
auto ptr = env->GetLongArrayElements(v, nullptr);
|
|
std::copy(ptr, ptr + len, ret.data());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, const CppType& v)
|
|
{
|
|
if constexpr (sizeof(Ty) == 1)
|
|
{
|
|
auto arr = env->NewByteArray(v.size());
|
|
auto ptr = env->GetByteArrayElements(arr, nullptr);
|
|
std::copy(v.begin(), v.end(), ptr);
|
|
return arr;
|
|
}
|
|
else if constexpr (sizeof(Ty) == 2)
|
|
{
|
|
auto arr = env->NewShortArray(v.size());
|
|
auto ptr = env->GetShortArrayElements(arr, nullptr);
|
|
std::copy(v.begin(), v.end(), ptr);
|
|
return arr;
|
|
}
|
|
else if constexpr (sizeof(Ty) == 4)
|
|
{
|
|
auto arr = env->NewIntArray(v.size());
|
|
auto ptr = env->GetIntArrayElements(arr, nullptr);
|
|
std::copy(v.begin(), v.end(), ptr);
|
|
return arr;
|
|
}
|
|
else if constexpr (sizeof(Ty) == 8)
|
|
{
|
|
auto arr = env->NewLongArray(v.size());
|
|
auto ptr = env->GetLongArrayElements(arr, nullptr);
|
|
std::copy(v.begin(), v.end(), ptr);
|
|
return arr;
|
|
}
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ValueBuilder<std::vector<char16_t>>
|
|
{
|
|
using CppType = std::vector<char16_t>;
|
|
using JniType = jcharArray;
|
|
static constexpr auto typeStr = "[C"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v)
|
|
{
|
|
throw std::bad_optional_access{};
|
|
}
|
|
size_t len = env->GetArrayLength(v);
|
|
std::vector<char16_t> ret(len);
|
|
auto ptr = env->GetCharArrayElements(v, nullptr);
|
|
std::copy(ptr, ptr + len, ret.data());
|
|
return ret;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, const CppType& v)
|
|
{
|
|
auto arr = env->NewCharArray(v.size());
|
|
auto ptr = env->GetCharArrayElements(arr, nullptr);
|
|
std::copy(v.begin(), v.end(), ptr);
|
|
return arr;
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<std::vector<Ty>, std::enable_if_t<std::is_floating_point_v<Ty>>>
|
|
{
|
|
using CppType = std::vector<Ty>;
|
|
using JniType = std::conditional_t<sizeof(Ty) == 4, jfloatArray, jdoubleArray>;
|
|
static constexpr auto typeStr = sizeof(Ty) == 4 ? "[F"sv : "[D"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v)
|
|
{
|
|
throw std::bad_optional_access{};
|
|
}
|
|
size_t len = env->GetArrayLength(v);
|
|
std::vector<Ty> ret(len);
|
|
if constexpr (sizeof(Ty) == 4)
|
|
{
|
|
auto ptr = env->GetFloatArrayElements(v, nullptr);
|
|
std::copy(ptr, ptr + len, ret.data());
|
|
}
|
|
else
|
|
{
|
|
auto ptr = env->GetDoubleArrayElements(v, nullptr);
|
|
std::copy(ptr, ptr + len, ret.data());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, const CppType& v)
|
|
{
|
|
if constexpr (sizeof(Ty) == 4)
|
|
{
|
|
auto arr = env->NewFloatArray(v.size());
|
|
auto ptr = env->GetFloatArrayElements(arr, nullptr);
|
|
std::copy(v.begin(), v.end(), ptr);
|
|
return arr;
|
|
}
|
|
else
|
|
{
|
|
auto arr = env->NewDoubleArray(v.size());
|
|
auto ptr = env->GetDoubleArrayElements(arr, nullptr);
|
|
std::copy(v.begin(), v.end(), ptr);
|
|
return arr;
|
|
}
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
struct ValueBuilder<JIterator<Ty>>
|
|
{
|
|
using CppType = JIterator<Ty>;
|
|
using JniType = jobject;
|
|
static constexpr auto typeStr = "Ljava/util/Iterator;"sv;
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v)
|
|
{
|
|
return CppType{ env, v };
|
|
}
|
|
if (!env->IsInstanceOf(v, JIteratorBase::jClass)) throw std::runtime_error{ StringConcat_v<svNotInstanceOf, typeStr, svNullTerm>.data()};
|
|
return CppType{ env, v };
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
bool JIterator<Ty>::hasNext()
|
|
{
|
|
return env->CallBooleanMethod(inst, jHasNext);
|
|
}
|
|
|
|
template<class Ty>
|
|
Ty JIterator<Ty>::next()
|
|
{
|
|
auto ret = env->CallObjectMethod(inst, jNext);
|
|
return ValueBuilder<Ty>{}.fromJava(env, ret);
|
|
}
|
|
|
|
template<class Ty>
|
|
Ty& JRef<Ty>::get()
|
|
{
|
|
return ValueBuilder<Ty>{}.fromJava(env, inst);
|
|
}
|
|
|
|
template<class Ty>
|
|
const Ty& JRef<Ty>::get() const
|
|
{
|
|
return ValueBuilder<Ty>{}.fromJava(env, inst);
|
|
}
|
|
|
|
template<class Ty>
|
|
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<Ty>>;
|
|
|
|
namespace detail
|
|
{
|
|
template <typename T>
|
|
struct IsFunctionObjectImpl
|
|
{
|
|
private:
|
|
using Yes = char(&)[1];
|
|
using No = char(&)[2];
|
|
|
|
struct Fallback
|
|
{
|
|
void operator()();
|
|
};
|
|
|
|
struct Derived : T, Fallback
|
|
{
|
|
};
|
|
|
|
template <typename U, U>
|
|
struct Check;
|
|
|
|
template <typename>
|
|
static Yes Test(...);
|
|
|
|
template <typename C>
|
|
static No Test(Check<void(Fallback::*)(), &C::operator()>*);
|
|
|
|
public:
|
|
static constexpr bool value{ sizeof(Test<Derived>(0)) == sizeof(Yes) };
|
|
};
|
|
|
|
template <class T, class ClsOverride>
|
|
struct CppWrapperImpl;
|
|
|
|
/* global function object */
|
|
template <typename R, typename... Ts, class ClsOverride>
|
|
struct CppWrapperImpl<R(Ts...), ClsOverride>
|
|
{
|
|
using Type = R(Ts...);
|
|
using FunctionPointerType = R(*)(Ts...);
|
|
using ReturnType = R;
|
|
using ClassType = void;
|
|
using ArgsTuple = std::tuple<Ts...>;
|
|
|
|
template <std::size_t N>
|
|
using Arg = typename std::tuple_element<N, ArgsTuple>::type;
|
|
|
|
static const std::size_t nargs{ sizeof...(Ts) };
|
|
|
|
};
|
|
|
|
/* global function pointer */
|
|
template <typename R, typename... Ts, class ClsOverride>
|
|
struct CppWrapperImpl<R(*)(Ts...), ClsOverride>
|
|
{
|
|
using Type = R(*)(Ts...);
|
|
using FunctionPointerType = R(*)(Ts...);
|
|
using ReturnType = R;
|
|
using ClassType = std::conditional_t<std::is_same_v<ClsOverride, void>, void, ClsOverride>;
|
|
using ArgsTuple = std::tuple<Ts...>;
|
|
|
|
template <std::size_t N>
|
|
using Arg = typename std::tuple_element<N, ArgsTuple>::type;
|
|
|
|
static const std::size_t nargs{ sizeof...(Ts) };
|
|
|
|
static constexpr auto typeStr = StringConcat_v<svLParen, toJniTypeStr<remove_cvref_t<Ts>>..., svRParen, toJniTypeStr<R>>;
|
|
|
|
using JniType = ToJniType<R>(*)(JNIEnv*, jobject, ToJniType<remove_cvref_t<Ts>>...);
|
|
|
|
template<Type func>
|
|
static constexpr JniType method()
|
|
{
|
|
return [](JNIEnv* env, jobject obj, ToJniType<remove_cvref_t<Ts>>... args) -> ToJniType<R>
|
|
{
|
|
return handleExc(env, [&]() -> ToJniType<R>
|
|
{
|
|
if constexpr (std::is_same_v<R, void>)
|
|
{
|
|
(*func)(ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
}
|
|
else
|
|
{
|
|
auto ret = (*func)(ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
return ValueBuilder<R>{}.toJava(env, std::move(ret));
|
|
}
|
|
});
|
|
};
|
|
}
|
|
};
|
|
|
|
/* member function pointer */
|
|
template <typename C, typename R, typename... Ts, class ClsOverride>
|
|
struct CppWrapperImpl<R(C::*)(Ts...), ClsOverride>
|
|
{
|
|
using Type = R(C::*)(Ts...);
|
|
using FunctionPointerType = R(*)(C*, Ts...);
|
|
using ReturnType = R;
|
|
using ClassType = std::conditional_t<std::is_same_v<ClsOverride, void>, C, ClsOverride>;;
|
|
using ArgsTuple = std::tuple<Ts...>;
|
|
|
|
template <std::size_t N>
|
|
using Arg = typename std::tuple_element<N, ArgsTuple>::type;
|
|
|
|
static constexpr std::size_t nargs{ sizeof...(Ts) };
|
|
|
|
static constexpr auto typeStr = StringConcat_v<svLParen, toJniTypeStr<remove_cvref_t<Ts>>..., svRParen, toJniTypeStr<R>>;
|
|
|
|
using JniType = ToJniType<R>(*)(JNIEnv*, jobject, ToJniType<remove_cvref_t<Ts>>...);
|
|
|
|
template<Type func>
|
|
static constexpr JniType method()
|
|
{
|
|
static_assert(std::is_base_of_v<JObject<ClassType>, ClassType>, "Only methods of JObject can be registered.");
|
|
|
|
return [](JNIEnv* env, jobject obj, ToJniType<remove_cvref_t<Ts>>... args) -> ToJniType<R>
|
|
{
|
|
return handleExc(env, [&]() -> ToJniType<R>
|
|
{
|
|
auto ptr = (ClassType*)env->GetLongField(obj, JObject<ClassType>::jInstField);
|
|
if (!ptr) throw std::runtime_error{ "Object is already closed or not initialized." };
|
|
|
|
if constexpr (std::is_same_v<R, void>)
|
|
{
|
|
(ptr->*func)(ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
}
|
|
else
|
|
{
|
|
auto ret = (ptr->*func)(ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
return ValueBuilder<R>{}.toJava(env, std::move(ret));
|
|
}
|
|
});
|
|
};
|
|
}
|
|
};
|
|
|
|
/* const member function pointer */
|
|
template <typename C, typename R, typename... Ts, class ClsOverride>
|
|
struct CppWrapperImpl<R(C::*)(Ts...) const, ClsOverride>
|
|
{
|
|
using Type = R(C::*)(Ts...) const;
|
|
using FunctionPointerType = R(*)(C*, Ts...);
|
|
using ReturnType = R;
|
|
using ClassType = std::conditional_t<std::is_same_v<ClsOverride, void>, C, ClsOverride>;;
|
|
using ArgsTuple = std::tuple<Ts...>;
|
|
|
|
template <std::size_t N>
|
|
using Arg = typename std::tuple_element<N, ArgsTuple>::type;
|
|
|
|
static constexpr std::size_t nargs{ sizeof...(Ts) };
|
|
|
|
static constexpr auto typeStr = StringConcat_v<svLParen, toJniTypeStr<remove_cvref_t<Ts>>..., svRParen, toJniTypeStr<R>>;
|
|
|
|
using JniType = ToJniType<R>(*)(JNIEnv*, jobject, ToJniType<remove_cvref_t<Ts>>...);
|
|
|
|
template<Type func>
|
|
static constexpr JniType method()
|
|
{
|
|
static_assert(std::is_base_of_v<JObject<ClassType>, ClassType>, "Only methods of JObject can be registered.");
|
|
|
|
return [](JNIEnv* env, jobject obj, ToJniType<remove_cvref_t<Ts>>... args) -> ToJniType<R>
|
|
{
|
|
return handleExc(env, [&]() -> ToJniType<R>
|
|
{
|
|
auto ptr = (ClassType*)env->GetLongField(obj, JObject<ClassType>::jInstField);
|
|
if (!ptr) throw std::runtime_error{ "Object is already closed or not initialized." };
|
|
|
|
if constexpr (std::is_same_v<R, void>)
|
|
{
|
|
(ptr->*func)(ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
}
|
|
else
|
|
{
|
|
auto ret = (ptr->*func)(ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
return ValueBuilder<R>{}.toJava(env, std::move(ret));
|
|
}
|
|
});
|
|
};
|
|
}
|
|
};
|
|
|
|
template <typename R, typename... Ts, class ClsOverride>
|
|
struct CppWrapperImpl<R(*)(jobject, Ts...), ClsOverride>
|
|
{
|
|
using Type = R(*)(jobject, Ts...);
|
|
using FunctionPointerType = R(*)(jobject, Ts...);
|
|
using ReturnType = R;
|
|
using ClassType = std::conditional_t<std::is_same_v<ClsOverride, void>, void, ClsOverride>;
|
|
using ArgsTuple = std::tuple<Ts...>;
|
|
|
|
template <std::size_t N>
|
|
using Arg = typename std::tuple_element<N, ArgsTuple>::type;
|
|
|
|
static const std::size_t nargs{ sizeof...(Ts) };
|
|
|
|
static constexpr auto typeStr = StringConcat_v<svLParen, toJniTypeStr<remove_cvref_t<Ts>>..., svRParen, toJniTypeStr<R>>;
|
|
|
|
using JniType = ToJniType<R>(*)(JNIEnv*, jobject, ToJniType<remove_cvref_t<Ts>>...);
|
|
|
|
template<Type func>
|
|
static constexpr JniType method()
|
|
{
|
|
return [](JNIEnv* env, jobject obj, ToJniType<remove_cvref_t<Ts>>... args) -> ToJniType<R>
|
|
{
|
|
return handleExc(env, [&]() -> ToJniType<R>
|
|
{
|
|
if constexpr (std::is_same_v<R, void>)
|
|
{
|
|
(*func)(obj, ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
}
|
|
else
|
|
{
|
|
auto ret = (*func)(obj, ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
return ValueBuilder<R>{}.toJava(env, std::move(ret));
|
|
}
|
|
});
|
|
};
|
|
}
|
|
};
|
|
|
|
/* member function pointer */
|
|
template <typename C, typename R, typename... Ts, class ClsOverride>
|
|
struct CppWrapperImpl<R(C::*)(JRef<C>, Ts...), ClsOverride>
|
|
{
|
|
using Type = R(C::*)(Ts...);
|
|
using FunctionPointerType = R(*)(C*, JRef<C>, Ts...);
|
|
using ReturnType = R;
|
|
using ClassType = std::conditional_t<std::is_same_v<ClsOverride, void>, C, ClsOverride>;;
|
|
using ArgsTuple = std::tuple<Ts...>;
|
|
|
|
template <std::size_t N>
|
|
using Arg = typename std::tuple_element<N, ArgsTuple>::type;
|
|
|
|
static constexpr std::size_t nargs{ sizeof...(Ts) };
|
|
|
|
static constexpr auto typeStr = StringConcat_v<svLParen, toJniTypeStr<remove_cvref_t<Ts>>..., svRParen, toJniTypeStr<R>>;
|
|
|
|
using JniType = ToJniType<R>(*)(JNIEnv*, jobject, ToJniType<remove_cvref_t<Ts>>...);
|
|
|
|
template<Type func>
|
|
static constexpr JniType method()
|
|
{
|
|
static_assert(std::is_base_of_v<JObject<ClassType>, ClassType>, "Only methods of JObject can be registered.");
|
|
|
|
return [](JNIEnv* env, jobject obj, ToJniType<remove_cvref_t<Ts>>... args) -> ToJniType<R>
|
|
{
|
|
return handleExc(env, [&]() -> ToJniType<R>
|
|
{
|
|
auto ptr = (ClassType*)env->GetLongField(obj, JObject<ClassType>::jInstField);
|
|
if (!ptr) throw std::runtime_error{ "Object is already closed or not initialized." };
|
|
|
|
if constexpr (std::is_same_v<R, void>)
|
|
{
|
|
(ptr->*func)(JRef<C>{env, obj}, ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
}
|
|
else
|
|
{
|
|
auto ret = (ptr->*func)(JRef<C>{env, obj}, ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
return ValueBuilder<R>{}.toJava(env, std::move(ret));
|
|
}
|
|
});
|
|
};
|
|
}
|
|
};
|
|
|
|
/* const member function pointer */
|
|
template <typename C, typename R, typename... Ts, class ClsOverride>
|
|
struct CppWrapperImpl<R(C::*)(JRef<C>, Ts...) const, ClsOverride>
|
|
{
|
|
using Type = R(C::*)(JRef<C>, Ts...) const;
|
|
using FunctionPointerType = R(*)(C*, JRef<C>, Ts...);
|
|
using ReturnType = R;
|
|
using ClassType = std::conditional_t<std::is_same_v<ClsOverride, void>, C, ClsOverride>;;
|
|
using ArgsTuple = std::tuple<Ts...>;
|
|
|
|
template <std::size_t N>
|
|
using Arg = typename std::tuple_element<N, ArgsTuple>::type;
|
|
|
|
static constexpr std::size_t nargs{ sizeof...(Ts) };
|
|
|
|
static constexpr auto typeStr = StringConcat_v<svLParen, toJniTypeStr<remove_cvref_t<Ts>>..., svRParen, toJniTypeStr<R>>;
|
|
|
|
using JniType = ToJniType<R>(*)(JNIEnv*, jobject, ToJniType<remove_cvref_t<Ts>>...);
|
|
|
|
template<Type func>
|
|
static constexpr JniType method()
|
|
{
|
|
static_assert(std::is_base_of_v<JObject<ClassType>, ClassType>, "Only methods of JObject can be registered.");
|
|
|
|
return [](JNIEnv* env, jobject obj, ToJniType<remove_cvref_t<Ts>>... args) -> ToJniType<R>
|
|
{
|
|
return handleExc(env, [&]() -> ToJniType<R>
|
|
{
|
|
auto ptr = (ClassType*)env->GetLongField(obj, JObject<ClassType>::jInstField);
|
|
if (!ptr) throw std::runtime_error{ "Object is already closed or not initialized." };
|
|
|
|
if constexpr (std::is_same_v<R, void>)
|
|
{
|
|
(ptr->*func)(JRef<C>{env, obj}, ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
}
|
|
else
|
|
{
|
|
auto ret = (ptr->*func)(JRef<C>{env, obj}, ValueBuilder<remove_cvref_t<Ts>>{}.fromJava(env, args)...);
|
|
return ValueBuilder<R>{}.toJava(env, std::move(ret));
|
|
}
|
|
});
|
|
};
|
|
}
|
|
};
|
|
|
|
/* member variable pointer */
|
|
template <typename C, typename R, class ClsOverride>
|
|
struct CppWrapperImpl<R(C::*), ClsOverride>
|
|
{
|
|
using Type = R(C::*);
|
|
using ReturnType = R;
|
|
using ClassType = std::conditional_t<std::is_same_v<ClsOverride, void>, C, ClsOverride>;
|
|
};
|
|
|
|
}
|
|
|
|
template <typename T>
|
|
struct IsFunctionObject : std::conditional<
|
|
std::is_class<T>::value,
|
|
detail::IsFunctionObjectImpl<T>,
|
|
std::false_type
|
|
>::type
|
|
{
|
|
};
|
|
|
|
template <typename T, class ClsOverride = void, class = void>
|
|
struct CppWrapper : detail::CppWrapperImpl<T, ClsOverride>
|
|
{
|
|
};
|
|
|
|
template <typename T, class ClsOverride>
|
|
struct CppWrapper<T, ClsOverride, std::enable_if_t<IsFunctionObject<T>::value>> :
|
|
detail::CppWrapperImpl<decltype(&T::operator()), ClsOverride>
|
|
{
|
|
};
|
|
|
|
template<class Func>
|
|
inline auto handleExc(JNIEnv* env, Func&& func)
|
|
{
|
|
try
|
|
{
|
|
return func();
|
|
}
|
|
catch (const std::bad_optional_access& e)
|
|
{
|
|
jclass exc = env->FindClass("java/lang/NullPointerException");
|
|
env->ThrowNew(exc, e.what());
|
|
}
|
|
catch (const std::invalid_argument& e)
|
|
{
|
|
jclass exc = env->FindClass("java/lang/IllegalArgumentException");
|
|
env->ThrowNew(exc, e.what());
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
jclass exc = env->FindClass("java/lang/Exception");
|
|
env->ThrowNew(exc, e.what());
|
|
}
|
|
|
|
if constexpr (!std::is_same_v<decltype(func()), void>)
|
|
{
|
|
return decltype(func()){};
|
|
}
|
|
}
|
|
|
|
template<class Ty, class... Args>
|
|
constexpr auto makeCtorDef()
|
|
{
|
|
using FuncPtr = void(*)(JNIEnv*, jobject, ToJniType<Args>...);
|
|
|
|
return NativeMethod<FuncPtr>{ "ctor", StringConcat_v<svLParen, toJniTypeStr<Args>..., svRParen, svV, svNullTerm>.data(),
|
|
(FuncPtr)[](JNIEnv* env, jobject obj, ToJniType<Args>... args)
|
|
{
|
|
return handleExc(env, [&]()
|
|
{
|
|
auto ptr = new Ty(ValueBuilder<Args>{}.fromJava(env, args)...);
|
|
env->SetLongField(obj, JObject<Ty>::jInstField, (jlong)ptr);
|
|
return;
|
|
});
|
|
} };
|
|
}
|
|
|
|
template<class Ty, class... Args>
|
|
static constexpr NativeMethod ctorDef = makeCtorDef<Ty, Args...>();
|
|
|
|
template<class Ty>
|
|
constexpr auto makeDtorDef()
|
|
{
|
|
using FuncPtr = void(*)(JNIEnv*, jobject);
|
|
|
|
return NativeMethod<FuncPtr>{ "close", "()V", (FuncPtr)[](JNIEnv* env, jobject obj)
|
|
{
|
|
return handleExc(env, [&]()
|
|
{
|
|
auto ptr = (Ty*)env->GetLongField(obj, JObject<Ty>::jInstField);
|
|
if (ptr)
|
|
{
|
|
delete ptr;
|
|
env->SetLongField(obj, JObject<Ty>::jInstField, 0);
|
|
}
|
|
return;
|
|
});
|
|
} };
|
|
}
|
|
|
|
template<class Ty>
|
|
static constexpr NativeMethod dtorDef = makeDtorDef<Ty>();
|
|
|
|
template<class Ty, auto memFn>
|
|
constexpr auto makeMethodDef()
|
|
{
|
|
using FuncPtr = decltype(CppWrapper<decltype(memFn), Ty>::template method<memFn>());
|
|
return NativeMethod<FuncPtr>{ nullptr, CppWrapper<decltype(memFn), Ty>::typeStr.data(), CppWrapper<decltype(memFn), Ty>::template method<memFn>()};
|
|
}
|
|
|
|
template<class Ty, auto memFn>
|
|
static constexpr NativeMethod methodDef = makeMethodDef<Ty, memFn>();
|
|
|
|
template<class Ty, const auto& ... methods>
|
|
class ClassDefinition
|
|
{
|
|
friend class Module;
|
|
public:
|
|
std::vector<const char*> methodNames;
|
|
|
|
using Class = Ty;
|
|
static_assert(std::is_base_of_v<JObject<Class>, Class>, "Only JObject has its ClassDefinition.");
|
|
|
|
inline static std::array<JNINativeMethod, sizeof...(methods)> methodDefs{ ((JNINativeMethod)methods)... };
|
|
|
|
constexpr ClassDefinition(const std::vector<const char*>& _methodNames = {}) : methodNames{ _methodNames } {}
|
|
|
|
template<class... Args>
|
|
constexpr ClassDefinition<Ty, methods..., ctorDef<Ty, Args...>> ctor() const
|
|
{
|
|
return { methodNames };
|
|
}
|
|
|
|
constexpr ClassDefinition<Ty, methods..., dtorDef<Ty>> dtor() const
|
|
{
|
|
return { methodNames };
|
|
}
|
|
|
|
template<auto memFn>
|
|
constexpr ClassDefinition<Ty, methods..., methodDef<Ty, memFn>> method(const char* name) const
|
|
{
|
|
auto ret = ClassDefinition<Ty, methods..., methodDef<Ty, memFn>>{ methodNames };
|
|
ret.methodNames.emplace_back(name);
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
template<class Ty, auto... memPtrs>
|
|
class DataClassDefinition
|
|
{
|
|
friend class Module;
|
|
public:
|
|
using Class = Ty;
|
|
using MemPtrTypes = std::tuple<decltype(memPtrs)...>;
|
|
static constexpr MemPtrTypes properties = std::make_tuple(memPtrs...);
|
|
|
|
std::vector<const char*> propertyNames;
|
|
|
|
static jclass jClass;
|
|
static jmethodID jInitMethod;
|
|
static std::array<jfieldID, sizeof...(memPtrs)> jFields;
|
|
|
|
constexpr DataClassDefinition(const std::vector<const char*>& _propertyNames = {}) : propertyNames{ _propertyNames } {}
|
|
|
|
template<auto newMemPtr>
|
|
constexpr DataClassDefinition<Ty, memPtrs..., newMemPtr> property(const char* name) const
|
|
{
|
|
auto ret = DataClassDefinition<Ty, memPtrs..., newMemPtr>{ propertyNames };
|
|
ret.propertyNames.emplace_back(name);
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
template<class Ty, auto... memPtrs>
|
|
jclass DataClassDefinition<Ty, memPtrs...>::jClass = nullptr;
|
|
|
|
template<class Ty, auto... memPtrs>
|
|
jmethodID DataClassDefinition<Ty, memPtrs...>::jInitMethod = nullptr;
|
|
|
|
template<class Ty, auto... memPtrs>
|
|
std::array<jfieldID, sizeof...(memPtrs)> DataClassDefinition<Ty, memPtrs...>::jFields;
|
|
|
|
template<class Ty, auto... memPtrs>
|
|
struct ValueBuilder<DataClassDefinition<Ty, memPtrs...>>
|
|
{
|
|
using DefTy = DataClassDefinition<Ty, memPtrs...>;
|
|
using CppType = Ty;
|
|
using JniType = jobject;
|
|
|
|
template<class VTy>
|
|
bool getProperty(JNIEnv* env, jobject obj, jfieldID field, VTy& v)
|
|
{
|
|
using JniFieldType = typename ValueBuilder<VTy>::JniType;
|
|
if constexpr (std::is_same_v<JniFieldType, jbyte>)
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, env->GetByteField(obj, field));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jshort>)
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, env->GetShortField(obj, field));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jint>)
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, env->GetIntField(obj, field));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jlong>)
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, env->GetLongField(obj, field));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jfloat>)
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, env->GetFloatField(obj, field));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jdouble>)
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, env->GetDoubleField(obj, field));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jboolean>)
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, env->GetBooleanField(obj, field));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jchar>)
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, env->GetCharField(obj, field));
|
|
}
|
|
else
|
|
{
|
|
v = ValueBuilder<VTy>{}.fromJava(env, (JniFieldType)env->GetObjectField(obj, field));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<size_t... idx>
|
|
bool getProperties(JNIEnv* env, jobject obj, CppType& v, std::index_sequence<idx...>)
|
|
{
|
|
return (... && getProperty(env, obj, DefTy::jFields[idx], v.*memPtrs));
|
|
}
|
|
|
|
CppType fromJava(JNIEnv* env, JniType v)
|
|
{
|
|
if (!v) throw std::bad_optional_access{};
|
|
if (!env->IsInstanceOf(v, DefTy::jClass)) throw std::runtime_error{ StringConcat_v<svNotInstanceOf, jclassName<CppType>, svNullTerm>.data()};
|
|
CppType ret;
|
|
if (!getProperties(env, v, ret, std::make_index_sequence<sizeof...(memPtrs)>{}))
|
|
{
|
|
throw std::runtime_error{ "Failed to get fields of " + std::string{jclassName<CppType>} + "." };
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
template<class VTy>
|
|
bool setProperty(JNIEnv* env, jobject obj, jfieldID field, const VTy& v)
|
|
{
|
|
using JniFieldType = typename ValueBuilder<VTy>::JniType;
|
|
if constexpr (std::is_same_v<JniFieldType, jbyte>)
|
|
{
|
|
env->SetByteField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jshort>)
|
|
{
|
|
env->SetShortField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jint>)
|
|
{
|
|
env->SetIntField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jlong>)
|
|
{
|
|
env->SetLongField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jfloat>)
|
|
{
|
|
env->SetFloatField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jdouble>)
|
|
{
|
|
env->SetDoubleField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jboolean>)
|
|
{
|
|
env->SetBooleanField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
else if constexpr (std::is_same_v<JniFieldType, jchar>)
|
|
{
|
|
env->SetCharField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
else
|
|
{
|
|
env->SetObjectField(obj, field, ValueBuilder<VTy>{}.toJava(env, v));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<size_t... idx>
|
|
bool setProperties(JNIEnv* env, jobject obj, const CppType& v, std::index_sequence<idx...>)
|
|
{
|
|
return (... && setProperty(env, obj, DefTy::jFields[idx], v.*memPtrs));
|
|
}
|
|
|
|
JniType toJava(JNIEnv* env, const CppType& v)
|
|
{
|
|
auto obj = env->NewObject(DefTy::jClass, DefTy::jInitMethod);
|
|
if (!setProperties(env, obj, v, std::make_index_sequence<sizeof...(memPtrs)>{}))
|
|
{
|
|
throw std::runtime_error{ "Failed to set fields of " + std::string{jclassName<CppType>} + "." };
|
|
}
|
|
return obj;
|
|
}
|
|
};
|
|
|
|
template<class Ty>
|
|
constexpr auto define()
|
|
{
|
|
return ClassDefinition<Ty>{}.dtor();
|
|
}
|
|
|
|
template<class Ty>
|
|
struct IsClassDefinition : std::false_type {};
|
|
|
|
template<class Ty, const auto& ... methods>
|
|
struct IsClassDefinition<ClassDefinition<Ty, methods...>> : std::true_type {};
|
|
|
|
template<class Ty>
|
|
struct IsDataClassDefinition : std::false_type {};
|
|
|
|
template<class Ty, auto ... memPtrs>
|
|
struct IsDataClassDefinition<DataClassDefinition<Ty, memPtrs...>> : std::true_type {};
|
|
|
|
class Module
|
|
{
|
|
friend class ClassDefiner;
|
|
int javaVersion;
|
|
std::vector<const char*> addedClasses;
|
|
|
|
template<class Type, class Class>
|
|
bool setPropertyId(jfieldID& out, JNIEnv* env, jclass cls, const char* name, Type(Class::*ptr))
|
|
{
|
|
out = env->GetFieldID(cls, name, toJniTypeStr<Type>.data());
|
|
if (!out) return false;
|
|
return true;
|
|
}
|
|
|
|
template<class DefTy, size_t... idx, class... MemPtrTypes>
|
|
bool fetchPropertyIds(DefTy& def, JNIEnv* env, jclass cls, std::index_sequence<idx...>, std::tuple<MemPtrTypes...> memPtrs)
|
|
{
|
|
return (... && setPropertyId(def.jFields[idx], env, cls, def.propertyNames[idx], std::get<idx>(memPtrs)));
|
|
}
|
|
|
|
template<class Definition>
|
|
bool addClass(JNIEnv* env, Definition&& def)
|
|
{
|
|
using DefTy = remove_cvref_t<Definition>;
|
|
|
|
static_assert(IsClassDefinition<DefTy>::value || IsDataClassDefinition<DefTy>::value,
|
|
"Only ClassDefinition or DataClassDefinition can be registered."
|
|
);
|
|
|
|
if constexpr (IsClassDefinition<DefTy>::value)
|
|
{
|
|
auto cls = JObject<typename DefTy::Class>::jClass
|
|
= (jclass)env->NewGlobalRef(env->FindClass(jclassName<typename DefTy::Class>.data()));
|
|
if (!cls) return false;
|
|
|
|
JObject<typename DefTy::Class>::jInstField = env->GetFieldID(cls, "_inst", "J");
|
|
if (!JObject<typename DefTy::Class>::jInstField)
|
|
{
|
|
std::cerr << jclassName<typename DefTy::Class> << " has no `_inst` field." << std::endl;
|
|
return false;
|
|
}
|
|
|
|
JObject<typename DefTy::Class>::jInitMethod = env->GetMethodID(cls, "<init>", "(J)V");
|
|
if (!JObject<typename DefTy::Class>::jInitMethod)
|
|
{
|
|
std::cerr << jclassName<typename DefTy::Class> << " has no constructor with a long argument" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
auto defs = (JNINativeMethod*)def.methodDefs.data();
|
|
auto size = def.methodDefs.size();
|
|
|
|
size_t m = 0;
|
|
for (size_t i = 0; i < size; ++i)
|
|
{
|
|
if (!defs[i].name)
|
|
{
|
|
defs[i].name = (char*)def.methodNames[m++];
|
|
}
|
|
}
|
|
|
|
if (env->RegisterNatives(cls, defs, size) != JNI_OK) return false;
|
|
|
|
addedClasses.emplace_back(jclassName<typename DefTy::Class>.data());
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
auto cls = JObject<typename DefTy::Class>::jClass
|
|
= def.jClass = (jclass)env->NewGlobalRef(env->FindClass(jclassName<typename DefTy::Class>.data()));
|
|
if (!cls) return false;
|
|
|
|
def.jInitMethod = env->GetMethodID(cls, "<init>", "()V");
|
|
if (!def.jInitMethod)
|
|
{
|
|
std::cerr << jclassName<typename DefTy::Class> << " has no default constructor." << std::endl;
|
|
return false;
|
|
}
|
|
|
|
auto idx = std::make_index_sequence<std::tuple_size_v<typename DefTy::MemPtrTypes>>{};
|
|
if (!fetchPropertyIds(def, env, cls, idx, def.properties)) return false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
Module(int _javaVersion) : javaVersion{ _javaVersion } {}
|
|
|
|
template<class ...Args>
|
|
jint load(JavaVM* vm, Args&&... args)
|
|
{
|
|
JNIEnv* env;
|
|
if (vm->GetEnv((void**)&env, javaVersion) != JNI_OK) return -1;
|
|
|
|
JIteratorBase::jClass = (jclass)env->NewGlobalRef(env->FindClass("java/util/Iterator"));
|
|
JIteratorBase::jHasNext = env->GetMethodID(JIteratorBase::jClass, "hasNext", "()Z");
|
|
JIteratorBase::jNext = env->GetMethodID(JIteratorBase::jClass, "next", "()Ljava/lang/Object;");
|
|
|
|
if (!(... && addClass(env, std::forward<Args>(args)))) return -1;
|
|
return javaVersion;
|
|
}
|
|
|
|
void unload(JavaVM* vm)
|
|
{
|
|
JNIEnv* env;
|
|
if (vm->GetEnv((void**)&env, javaVersion) != JNI_OK) return;
|
|
for (auto c : addedClasses)
|
|
{
|
|
auto cls = env->FindClass(c);
|
|
if (!cls) return;
|
|
|
|
env->UnregisterNatives(cls);
|
|
}
|
|
}
|
|
};
|
|
}
|