mirror of https://github.com/axmolengine/axmol.git
382 lines
14 KiB
C++
382 lines
14 KiB
C++
|
#pragma once
|
||
|
|
||
|
#include <jni/types.hpp>
|
||
|
#include <jni/errors.hpp>
|
||
|
#include <jni/functions.hpp>
|
||
|
#include <jni/tagging.hpp>
|
||
|
#include <jni/class.hpp>
|
||
|
#include <jni/object.hpp>
|
||
|
|
||
|
#include <exception>
|
||
|
#include <type_traits>
|
||
|
|
||
|
namespace jni
|
||
|
{
|
||
|
template < class M, class Enable = void >
|
||
|
struct NativeMethodTraits;
|
||
|
|
||
|
template < class R, class... Args >
|
||
|
struct NativeMethodTraits< R (Args...) >
|
||
|
{
|
||
|
using Type = R (Args...);
|
||
|
using ResultType = R;
|
||
|
};
|
||
|
|
||
|
template < class R, class... Args >
|
||
|
struct NativeMethodTraits< R (*)(Args...) >
|
||
|
: NativeMethodTraits< R (Args...) > {};
|
||
|
|
||
|
template < class T, class R, class... Args >
|
||
|
struct NativeMethodTraits< R (T::*)(Args...) const >
|
||
|
: NativeMethodTraits< R (Args...) > {};
|
||
|
|
||
|
template < class T, class R, class... Args >
|
||
|
struct NativeMethodTraits< R (T::*)(Args...) >
|
||
|
: NativeMethodTraits< R (Args...) > {};
|
||
|
|
||
|
template < class M >
|
||
|
struct NativeMethodTraits< M, std::enable_if_t< std::is_class<M>::value > >
|
||
|
: NativeMethodTraits< decltype(&M::operator()) > {};
|
||
|
|
||
|
|
||
|
/// Low-level, lambda
|
||
|
|
||
|
template < class M >
|
||
|
auto MakeNativeMethod(const char* name, const char* sig, const M& m,
|
||
|
std::enable_if_t< std::is_class<M>::value >* = nullptr)
|
||
|
{
|
||
|
using FunctionType = typename NativeMethodTraits<M>::Type;
|
||
|
using ResultType = typename NativeMethodTraits<M>::ResultType;
|
||
|
|
||
|
static FunctionType* method = m;
|
||
|
|
||
|
auto wrapper = [] (JNIEnv* env, auto... args)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
return method(env, args...);
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
ThrowJavaError(*env, std::current_exception());
|
||
|
return ResultType();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return JNINativeMethod< FunctionType > { name, sig, wrapper };
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Low-level, function pointer
|
||
|
|
||
|
template < class M, M method >
|
||
|
auto MakeNativeMethod(const char* name, const char* sig)
|
||
|
{
|
||
|
using FunctionType = typename NativeMethodTraits<M>::Type;
|
||
|
using ResultType = typename NativeMethodTraits<M>::ResultType;
|
||
|
|
||
|
auto wrapper = [] (JNIEnv* env, auto... args)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
return method(env, args...);
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
ThrowJavaError(*env, std::current_exception());
|
||
|
return ResultType();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return JNINativeMethod< FunctionType > { name, sig, wrapper };
|
||
|
}
|
||
|
|
||
|
|
||
|
/// High-level, lambda
|
||
|
|
||
|
template < class T, T*... >
|
||
|
struct NativeMethodMaker;
|
||
|
|
||
|
template < class T, class R, class Subject, class... Args >
|
||
|
struct NativeMethodMaker< R (T::*)(JNIEnv&, Subject, Args...) const >
|
||
|
{
|
||
|
template < class M >
|
||
|
auto operator()(const char* name, const M& m)
|
||
|
{
|
||
|
static M method(m);
|
||
|
|
||
|
auto wrapper = [] (JNIEnv* env, UntaggedType<Subject> subject, UntaggedType<Args>... args)
|
||
|
{
|
||
|
return ReleaseUnique(method(*env, AsLvalue(Tag<std::decay_t<Subject>>(*env, *subject)), AsLvalue(Tag<std::decay_t<Args>>(*env, args))...));
|
||
|
};
|
||
|
|
||
|
return MakeNativeMethod(name, TypeSignature<RemoveUniqueType<R> (std::decay_t<Args>...)>()(), wrapper);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template < class T, class Subject, class... Args >
|
||
|
struct NativeMethodMaker< void (T::*)(JNIEnv&, Subject, Args...) const >
|
||
|
{
|
||
|
template < class M >
|
||
|
auto operator()(const char* name, const M& m)
|
||
|
{
|
||
|
static M method(m);
|
||
|
|
||
|
auto wrapper = [] (JNIEnv* env, UntaggedType<Subject> subject, UntaggedType<Args>... args)
|
||
|
{
|
||
|
method(*env, AsLvalue(Tag<std::decay_t<Subject>>(*env, *subject)), AsLvalue(Tag<std::decay_t<Args>>(*env, args))...);
|
||
|
};
|
||
|
|
||
|
return MakeNativeMethod(name, TypeSignature<void (std::decay_t<Args>...)>()(), wrapper);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template < class M >
|
||
|
auto MakeNativeMethod(const char* name, const M& m)
|
||
|
{
|
||
|
return NativeMethodMaker<decltype(&M::operator())>()(name, m);
|
||
|
}
|
||
|
|
||
|
|
||
|
/// High-level, function pointer
|
||
|
|
||
|
template < class R, class Subject, class... Args, R (*method)(JNIEnv&, Subject, Args...) >
|
||
|
struct NativeMethodMaker< R (JNIEnv&, Subject, Args...), method >
|
||
|
{
|
||
|
auto operator()(const char* name)
|
||
|
{
|
||
|
auto wrapper = [] (JNIEnv* env, UntaggedType<Subject> subject, UntaggedType<Args>... args)
|
||
|
{
|
||
|
return ReleaseUnique(method(*env, AsLvalue(Tag<std::decay_t<Subject>>(*env, *subject)), AsLvalue(Tag<std::decay_t<Args>>(*env, args))...));
|
||
|
};
|
||
|
|
||
|
return MakeNativeMethod(name, TypeSignature<RemoveUniqueType<R> (std::decay_t<Args>...)>()(), wrapper);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template < class Subject, class... Args, void (*method)(JNIEnv&, Subject, Args...) >
|
||
|
struct NativeMethodMaker< void (JNIEnv&, Subject, Args...), method >
|
||
|
{
|
||
|
auto operator()(const char* name)
|
||
|
{
|
||
|
auto wrapper = [] (JNIEnv* env, UntaggedType<Subject> subject, UntaggedType<Args>... args)
|
||
|
{
|
||
|
method(*env, AsLvalue(Tag<std::decay_t<Subject>>(*env, *subject)), AsLvalue(Tag<std::decay_t<Args>>(*env, args))...);
|
||
|
};
|
||
|
|
||
|
return MakeNativeMethod(name, TypeSignature<void (std::decay_t<Args>...)>()(), wrapper);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template < class M, M method >
|
||
|
auto MakeNativeMethod(const char* name)
|
||
|
{
|
||
|
using FunctionType = typename NativeMethodTraits<M>::Type;
|
||
|
return NativeMethodMaker<FunctionType, method>()(name);
|
||
|
}
|
||
|
|
||
|
|
||
|
/// High-level peer, lambda
|
||
|
|
||
|
template < class L, class >
|
||
|
class NativePeerLambdaMethod;
|
||
|
|
||
|
template < class L, class R, class P, class... Args >
|
||
|
class NativePeerLambdaMethod< L, R (L::*)(JNIEnv&, P&, Args...) const >
|
||
|
{
|
||
|
private:
|
||
|
const char* name;
|
||
|
L lambda;
|
||
|
|
||
|
public:
|
||
|
NativePeerLambdaMethod(const char* n, const L& l)
|
||
|
: name(n), lambda(l)
|
||
|
{}
|
||
|
|
||
|
template < class Peer, class TagType, class = std::enable_if_t< std::is_same<P, Peer>::value > >
|
||
|
auto operator()(const Field<TagType, jlong>& field)
|
||
|
{
|
||
|
auto wrapper = [field, lambda = lambda] (JNIEnv& env, Object<TagType>& obj, Args... args)
|
||
|
{
|
||
|
return lambda(env, *reinterpret_cast<P*>(obj.Get(env, field)), args...);
|
||
|
};
|
||
|
|
||
|
return MakeNativeMethod(name, wrapper);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template < class L >
|
||
|
auto MakeNativePeerMethod(const char* name, const L& lambda,
|
||
|
std::enable_if_t< std::is_class<L>::value >* = nullptr)
|
||
|
{
|
||
|
return NativePeerLambdaMethod<L, decltype(&L::operator())>(name, lambda);
|
||
|
}
|
||
|
|
||
|
|
||
|
/// High-level peer, function pointer
|
||
|
|
||
|
template < class M, M* >
|
||
|
class NativePeerFunctionPointerMethod;
|
||
|
|
||
|
template < class R, class P, class... Args, R (*method)(JNIEnv&, P&, Args...) >
|
||
|
class NativePeerFunctionPointerMethod< R (JNIEnv&, P&, Args...), method >
|
||
|
{
|
||
|
private:
|
||
|
const char* name;
|
||
|
|
||
|
public:
|
||
|
NativePeerFunctionPointerMethod(const char* n)
|
||
|
: name(n)
|
||
|
{}
|
||
|
|
||
|
template < class Peer, class TagType, class = std::enable_if_t< std::is_same<P, Peer>::value > >
|
||
|
auto operator()(const Field<TagType, jlong>& field)
|
||
|
{
|
||
|
auto wrapper = [field] (JNIEnv& env, Object<TagType>& obj, Args... args)
|
||
|
{
|
||
|
auto ptr = reinterpret_cast<P*>(obj.Get(env, field));
|
||
|
if (ptr) return method(env, *ptr, args...);
|
||
|
ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"),
|
||
|
"invalid native peer");
|
||
|
};
|
||
|
|
||
|
return MakeNativeMethod(name, wrapper);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template < class M, M method >
|
||
|
auto MakeNativePeerMethod(const char* name,
|
||
|
std::enable_if_t< !std::is_member_function_pointer<M>::value >* = nullptr)
|
||
|
{
|
||
|
using FunctionType = typename NativeMethodTraits<M>::Type;
|
||
|
return NativePeerFunctionPointerMethod<FunctionType, method>(name);
|
||
|
}
|
||
|
|
||
|
/// High-level peer, member function pointer
|
||
|
|
||
|
template < class M, M>
|
||
|
class NativePeerMemberFunctionMethod;
|
||
|
|
||
|
template < class R, class P, class... Args, R (P::*method)(JNIEnv&, Args...)>
|
||
|
class NativePeerMemberFunctionMethod< R (P::*)(JNIEnv&, Args...), method>
|
||
|
{
|
||
|
private:
|
||
|
const char* name;
|
||
|
|
||
|
public:
|
||
|
NativePeerMemberFunctionMethod(const char* n)
|
||
|
: name(n)
|
||
|
{}
|
||
|
|
||
|
template < class Peer, class TagType, class = std::enable_if_t< std::is_same<P, Peer>::value > >
|
||
|
auto operator()(const Field<TagType, jlong>& field)
|
||
|
{
|
||
|
auto wrapper = [field] (JNIEnv& env, Object<TagType>& obj, Args... args)
|
||
|
{
|
||
|
auto ptr = reinterpret_cast<P*>(obj.Get(env, field));
|
||
|
if (ptr) return (ptr->*method)(env, args...);
|
||
|
ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"),
|
||
|
"invalid native peer");
|
||
|
};
|
||
|
return MakeNativeMethod(name, wrapper);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template < class M, M method>
|
||
|
auto MakeNativePeerMethod(const char* name,
|
||
|
std::enable_if_t< std::is_member_function_pointer<M>::value >* = nullptr)
|
||
|
{
|
||
|
return NativePeerMemberFunctionMethod<M, method>(name);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A registration function for native methods on a "native peer": a long-lived native
|
||
|
* object corresponding to a Java object, usually created when the Java object is created
|
||
|
* and destroyed when the Java object's finalizer runs.
|
||
|
*
|
||
|
* It assumes that the Java object has a field, named by `fieldName`, of Java type `long`,
|
||
|
* which is used to hold a pointer to the native peer.
|
||
|
*
|
||
|
* `Methods` must be a sequence of `NativePeerMethod` instances, instantiated with pointer
|
||
|
* to member functions of the native peer class. For each method in `methods`, a native
|
||
|
* method is bound with a signature corresponding to that of the member function. The
|
||
|
* wrapper for that native method obtains the native peer instance from the Java field and
|
||
|
* calls the native peer method on it, passing along any arguments.
|
||
|
*
|
||
|
* An overload is provided that accepts a Callable object with a unique_ptr result type and
|
||
|
* the names for native creation and finalization methods, allowing creation and disposal of
|
||
|
* the native peer from Java.
|
||
|
*
|
||
|
* For an example of all of the above, see the `examples` directory.
|
||
|
*/
|
||
|
|
||
|
template < class Peer, class TagType, class... Methods >
|
||
|
void RegisterNativePeer(JNIEnv& env, const Class<TagType>& clazz, const char* fieldName, Methods&&... methods)
|
||
|
{
|
||
|
static Field<TagType, jni::jlong> field { env, clazz, fieldName };
|
||
|
RegisterNatives(env, *clazz, methods.template operator()<Peer>(field)...);
|
||
|
}
|
||
|
|
||
|
template < class Peer, class TagType, class >
|
||
|
struct NativePeerHelper;
|
||
|
|
||
|
template < class Peer, class TagType, class... Args >
|
||
|
struct NativePeerHelper< Peer, TagType, std::unique_ptr<Peer> (JNIEnv&, Args...) >
|
||
|
{
|
||
|
using UniquePeer = std::unique_ptr<Peer>;
|
||
|
using Initializer = UniquePeer (JNIEnv&, Args...);
|
||
|
|
||
|
auto MakeInitializer(const Field<TagType, jlong>& field, const char* name, Initializer* initializer) const
|
||
|
{
|
||
|
auto wrapper = [field, initializer] (JNIEnv& e, Object<TagType>& obj, std::decay_t<Args>&... args)
|
||
|
{
|
||
|
UniquePeer previous(reinterpret_cast<Peer*>(obj.Get(e, field)));
|
||
|
UniquePeer instance(initializer(e, args...));
|
||
|
obj.Set(e, field, reinterpret_cast<jlong>(instance.get()));
|
||
|
instance.release();
|
||
|
};
|
||
|
|
||
|
return MakeNativeMethod(name, wrapper);
|
||
|
}
|
||
|
|
||
|
auto MakeFinalizer(const Field<TagType, jlong>& field, const char* name) const
|
||
|
{
|
||
|
auto wrapper = [field] (JNIEnv& e, Object<TagType>& obj)
|
||
|
{
|
||
|
UniquePeer instance(reinterpret_cast<Peer*>(obj.Get(e, field)));
|
||
|
if (instance) obj.Set(e, field, jlong(0));
|
||
|
instance.reset();
|
||
|
};
|
||
|
|
||
|
return MakeNativeMethod(name, wrapper);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template < class Peer, class TagType, class Initializer, class... Methods >
|
||
|
void RegisterNativePeer(JNIEnv& env, const Class<TagType>& clazz, const char* fieldName,
|
||
|
Initializer initialize,
|
||
|
const char* initializeMethodName,
|
||
|
const char* finalizeMethodName,
|
||
|
Methods&&... methods)
|
||
|
{
|
||
|
static Field<TagType, jlong> field { env, clazz, fieldName };
|
||
|
|
||
|
using InitializerMethodType = typename NativeMethodTraits<Initializer>::Type;
|
||
|
NativePeerHelper<Peer, TagType, InitializerMethodType> helper;
|
||
|
|
||
|
RegisterNatives(env, *clazz,
|
||
|
helper.MakeInitializer(field, initializeMethodName, initialize),
|
||
|
helper.MakeFinalizer(field, finalizeMethodName),
|
||
|
methods.template operator()<Peer>(field)...);
|
||
|
}
|
||
|
|
||
|
// Like std::make_unique, but with non-universal reference arguments, so it can be
|
||
|
// explicitly specialized (jni::MakePeer<Peer, jni::jboolean, ...>).
|
||
|
template < class Peer, class... Args >
|
||
|
std::unique_ptr<Peer> MakePeer(jni::JNIEnv& env, Args... args)
|
||
|
{
|
||
|
return std::make_unique<Peer>(env, args...);
|
||
|
}
|
||
|
}
|