From 316f8ef77d99ca50279890eb65ef916c01091eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 2 Jan 2018 16:59:42 -0800 Subject: [PATCH 1/2] free up jni::Tag name by renaming to AddTag/RemoveTag for clarity --- include/jni/array.hpp | 2 +- include/jni/class.hpp | 8 ++++---- include/jni/native_method.hpp | 4 ++-- include/jni/object.hpp | 12 ++++++------ include/jni/tagging.hpp | 12 ++++++------ 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/jni/array.hpp b/include/jni/array.hpp index 84d5972..1624605 100644 --- a/include/jni/array.hpp +++ b/include/jni/array.hpp @@ -121,7 +121,7 @@ namespace jni void Set(JNIEnv& env, jsize index, const ElementType& value) { - SetObjectArrayElement(env, SafeDereference(env, array), index, Untag(value)); + SetObjectArrayElement(env, SafeDereference(env, array), index, RemoveTag(value)); } static Array> New(JNIEnv& env, jsize length, const Class& clazz, const Object& initialElement = Object()) diff --git a/include/jni/class.hpp b/include/jni/class.hpp index 3649247..853dee6 100644 --- a/include/jni/class.hpp +++ b/include/jni/class.hpp @@ -50,7 +50,7 @@ namespace jni template < class... Args > Object New(JNIEnv& env, const Constructor& method, const Args&... args) const { - return Object(&NewObject(env, *clazz, method, Untag(args)...)); + return Object(&NewObject(env, *clazz, method, RemoveTag(args)...)); } template < class T > @@ -85,20 +85,20 @@ namespace jni auto Call(JNIEnv& env, const StaticMethod& method, const Args&... args) const -> std::enable_if_t< IsPrimitive::value, R > { - return CallStaticMethod(env, *clazz, method, Untag(args)...); + return CallStaticMethod(env, *clazz, method, RemoveTag(args)...); } template < class R, class... Args > auto Call(JNIEnv& env, const StaticMethod& method, const Args&... args) const -> std::enable_if_t< !IsPrimitive::value && !std::is_void::value, R > { - return R(reinterpret_cast>(CallStaticMethod(env, *clazz, method, Untag(args)...))); + return R(reinterpret_cast>(CallStaticMethod(env, *clazz, method, RemoveTag(args)...))); } template < class... Args > void Call(JNIEnv& env, const StaticMethod& method, const Args&... args) const { - CallStaticMethod(env, *clazz, method, Untag(args)...); + CallStaticMethod(env, *clazz, method, RemoveTag(args)...); } static Class Find(JNIEnv& env) diff --git a/include/jni/native_method.hpp b/include/jni/native_method.hpp index 082a7d1..8a5a292 100644 --- a/include/jni/native_method.hpp +++ b/include/jni/native_method.hpp @@ -109,7 +109,7 @@ namespace jni auto wrapper = [] (JNIEnv* env, UntaggedType subject, UntaggedType... args) -> UntaggedType { - return method(*env, Tag(*subject), Tag(args)...); + return method(*env, AddTag(*subject), AddTag(args)...); }; return MakeNativeMethod(name, TypeSignature()(), wrapper); @@ -132,7 +132,7 @@ namespace jni { auto wrapper = [] (JNIEnv* env, UntaggedType subject, UntaggedType... args) -> UntaggedType { - return method(*env, Tag(*subject), Tag(args)...); + return method(*env, AddTag(*subject), AddTag(args)...); }; return MakeNativeMethod(name, TypeSignature()(), wrapper); diff --git a/include/jni/object.hpp b/include/jni/object.hpp index 5e64089..9eeb2b1 100644 --- a/include/jni/object.hpp +++ b/include/jni/object.hpp @@ -106,40 +106,40 @@ namespace jni auto Call(JNIEnv& env, const Method& method, const Args&... args) const -> std::enable_if_t< IsPrimitive::value, R > { - return CallMethod(env, obj, method, Untag(args)...); + return CallMethod(env, obj, method, RemoveTag(args)...); } template < class R, class... Args > auto Call(JNIEnv& env, const Method& method, const Args&... args) const -> std::enable_if_t< !IsPrimitive::value && !std::is_void::value, R > { - return R(reinterpret_cast>(CallMethod(env, obj, method, Untag(args)...))); + return R(reinterpret_cast>(CallMethod(env, obj, method, RemoveTag(args)...))); } template < class... Args > void Call(JNIEnv& env, const Method& method, const Args&... args) const { - CallMethod(env, obj, method, Untag(args)...); + CallMethod(env, obj, method, RemoveTag(args)...); } template < class R, class... Args > auto CallNonvirtual(JNIEnv& env, const Class& clazz, const Method& method, const Args&... args) const -> std::enable_if_t< IsPrimitive::value, R > { - return CallNonvirtualMethod(env, obj, clazz, method, Untag(args)...); + return CallNonvirtualMethod(env, obj, clazz, method, RemoveTag(args)...); } template < class R, class... Args > auto CallNonvirtual(JNIEnv& env, const Class& clazz, const Method& method, const Args&... args) const -> std::enable_if_t< !IsPrimitive::value, R > { - return R(reinterpret_cast>(CallNonvirtualMethod(env, obj, clazz, method, Untag(args)...))); + return R(reinterpret_cast>(CallNonvirtualMethod(env, obj, clazz, method, RemoveTag(args)...))); } template < class... Args > void CallNonvirtual(JNIEnv& env, const Class& clazz, const Method& method, const Args&... args) const { - CallNonvirtualMethod(env, obj, clazz, method, Untag(args)...); + CallNonvirtualMethod(env, obj, clazz, method, RemoveTag(args)...); } UniqueObject NewGlobalRef(JNIEnv& env) const diff --git a/include/jni/tagging.hpp b/include/jni/tagging.hpp index 5ab2eee..fa55fdb 100644 --- a/include/jni/tagging.hpp +++ b/include/jni/tagging.hpp @@ -7,21 +7,21 @@ namespace jni { template < class T > - auto Tag(T primitive) + auto AddTag(T primitive) -> std::enable_if_t< IsPrimitive::value, T > { return primitive; } template < class T, class U > - auto Tag(U* u) + auto AddTag(U* u) -> std::enable_if_t< !IsPrimitive::value, T > { return T(u); } template < class T, class U > - auto Tag(U& u) + auto AddTag(U& u) -> std::enable_if_t< !IsPrimitive::value, T > { return T(u); @@ -29,14 +29,14 @@ namespace jni template < class T > - auto Untag(T primitive) + auto RemoveTag(T primitive) -> std::enable_if_t< IsPrimitive::value, T > { return primitive; } template < class T > - auto Untag(const T& t) + auto RemoveTag(const T& t) -> std::enable_if_t< !IsPrimitive::value, decltype(t.Get()) > { return t.Get(); @@ -46,7 +46,7 @@ namespace jni template < class T > struct UntaggedTypeTraits { - using Type = decltype(Untag(std::declval())); + using Type = decltype(RemoveTag(std::declval())); }; template <> From 05c82113aa784473cce69bea1b96767a7116dce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Wed, 3 Jan 2018 13:40:44 -0800 Subject: [PATCH 2/2] test even higher level bindings --- Makefile | 13 +++- examples/Binding.java | 17 +++++ examples/binding.cpp | 103 +++++++++++++++++++++++++++++ examples/binding.hpp | 117 +++++++++++++++++++++++++++++++++ include/jni/type_signature.hpp | 2 +- 5 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 examples/Binding.java create mode 100644 examples/binding.cpp create mode 100644 examples/binding.hpp diff --git a/Makefile b/Makefile index 4a09217..03e30a8 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,11 @@ libpeer.$(dylib)_SOURCES = examples/native_peer.cpp CXXFLAGS__examples/native_peer.cpp = -Wno-shadow libpeer.$(dylib)_LDFLAGS = $(LDFLAGS_shared) +TARGETS += libbinding.$(dylib) +libbinding.$(dylib)_SOURCES = examples/binding.cpp +CXXFLAGS__examples/binding.cpp = -Wno-shadow +libbinding.$(dylib)_LDFLAGS = $(LDFLAGS_shared) + .PHONY: all all: $(TARGETS) @@ -49,9 +54,15 @@ test: low_level high_level $(BUILD)/high_level .PHONY: examples -examples: libhello.$(dylib) examples/Hello.class libpeer.$(dylib) examples/NativePeer.class +examples: libhello.$(dylib) examples/Hello.class +examples: libpeer.$(dylib) examples/NativePeer.class +examples: libbinding.$(dylib) examples/Binding.class java -Djava.library.path=$(BUILD) -Xcheck:jni -cp examples Hello $(shell whoami) java -Djava.library.path=$(BUILD) -Xcheck:jni -cp examples NativePeer + java -Djava.library.path=$(BUILD) -Xcheck:jni -cp examples Binding + +example-binding: libbinding.$(dylib) examples/Binding.class + java -Djava.library.path=$(BUILD) -Xcheck:jni -cp examples Binding # -------------------------------------------------------------------------------------------------- diff --git a/examples/Binding.java b/examples/Binding.java new file mode 100644 index 0000000..0bf5dc9 --- /dev/null +++ b/examples/Binding.java @@ -0,0 +1,17 @@ +class HighLevelBinding { + public native void greet(String args); + public native void greet(int args); + public static native void greet(double args); + public int quadruple(int num) { + return num * 4; + } +} + +public class Binding { + public static void main(String[] args) { + System.loadLibrary("binding"); + new HighLevelBinding().greet("test"); + new HighLevelBinding().greet(4); + HighLevelBinding.greet(3.14); + } +} diff --git a/examples/binding.cpp b/examples/binding.cpp new file mode 100644 index 0000000..6561158 --- /dev/null +++ b/examples/binding.cpp @@ -0,0 +1,103 @@ +#include "binding.hpp" + +namespace java { namespace lang { struct Object; } } +namespace java { namespace lang { struct String; } } + +namespace jni { + +template <> struct Name { + using Tag = ObjectTag; +}; + +template <> struct Name { + using Tag = StringTag; +}; + +} // namespace jni + + +namespace java { +namespace lang { + +struct Object : jni::Binding { + using Binding::Binding; +}; + +struct String : jni::Binding { + using Binding::Binding; + + // Java methods + + jni::jint length(jni::JNIEnv& env) { + static const auto method = jni_GetMethod(env, "length"); + return self.Call(env, method); + } +}; + +} // namespace lang +} // namespace java + + + +struct HighLevelBinding; + +namespace jni { +template <> struct Name::Tag { + static constexpr auto Name() { return "HighLevelBinding"; } +}; +} // namespace jni + +struct HighLevelBinding : jni::Binding { + using Binding::Binding; + + HighLevelBinding(jni::JNIEnv& env) : Binding(jni_New<>(env)) {} + + // Java methods + jni::jint quadruple(jni::JNIEnv& env, jni::jint num) { + // Call Java method + static const auto method = jni_GetMethod(env, "quadruple"); + return self.Call(env, method, num); + } + + // Native methods + void greet(jni::JNIEnv&, java::lang::String args); + void greet(jni::JNIEnv&, jni::jint args); + static void greet(jni::JNIEnv&, jni::jdouble args); + + + static void jni_Register(jni::JNIEnv& env) { + jni::RegisterNatives(env, jni_Class(env), + jni_Bind::Method<&HighLevelBinding::greet>("greet"), + jni_Bind::Method<&HighLevelBinding::greet>("greet"), + jni_Bind::StaticMethod<&HighLevelBinding::greet>("greet") + ); + } +}; + + +void HighLevelBinding::greet(jni::JNIEnv& env, java::lang::String args) { + const auto test = jni::Make(env, args); + fprintf(stderr, "greet '%s' (length %d)\n", test.c_str(), args.length(env)); +} + +void HighLevelBinding::greet(jni::JNIEnv& env, jni::jint args) { + fprintf(stderr, "greet %d\n", quadruple(env, args)); +} + +void HighLevelBinding::greet(jni::JNIEnv&, jni::jdouble args) { + fprintf(stderr, "greet static %f\n", args); +} + + + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + jni::JNIEnv& env{ jni::GetEnv(*vm) }; + try { + java::lang::Object::jni_Register(env); + java::lang::String::jni_Register(env); + HighLevelBinding::jni_Register(env); + } catch (const jni::PendingJavaException&) { + jni::ExceptionDescribe(env); + } + return jni::Unwrap(jni::jni_version_1_2); +} diff --git a/examples/binding.hpp b/examples/binding.hpp new file mode 100644 index 0000000..a512097 --- /dev/null +++ b/examples/binding.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include + +namespace jni + { + + template < class T > + struct Name + { + struct Tag {}; + }; + + template < class T > + class Binding + { + using Tag = typename Name::Tag; + + public: + // Constructs from jobject* pointers directly. + explicit Binding(UntaggedType> self_) : self(self_) {} + + Binding(Object self_) : self(self_) {} + + static void jni_Register(jni::JNIEnv&) {} + + operator Object() const { return self; } + + protected: + static auto jni_Class(JNIEnv& env) + { + static auto javaClass = Class::Find(env).NewGlobalRef(env).release(); + return *javaClass; + } + + template < class... Args > + static auto jni_New(JNIEnv& env, Args&&... args) + { + static const auto constructor = jni_Class(env).template GetConstructor(env); + return jni_Class(env).New(env, constructor, std::forward(args)...); + } + + template < class Signature > + static auto jni_GetMethod(JNIEnv& env, const char* name) + { + return jni_Class(env).template GetMethod(env, name); + } + + template < class M > + struct jni_Bind; + + template < class R, class... Args > + struct jni_Bind< R (Args...) > { + template + static auto StaticMethod(const char* name) + { + auto wrapper = [](JNIEnv* env, UntaggedType>, UntaggedType... args) -> UntaggedType + { + try + { + return method(*env, AddTag(args)...); + } + catch (...) + { + ThrowJavaError(*env, std::current_exception()); + return UntaggedType(); + } + }; + + using FunctionType = typename NativeMethodTraits::Type; + return JNINativeMethod{ name, TypeSignature()(), wrapper }; + } + + template + static auto Method(const char* name) + { + using Subject = Object; + auto wrapper = [](JNIEnv* env, UntaggedType subject, UntaggedType... args) -> UntaggedType + { + try + { + return (T(AddTag(*subject)).*method)(*env, AddTag(args)...); + } + catch (...) + { + ThrowJavaError(*env, std::current_exception()); + return UntaggedType(); + } + }; + + using FunctionType = typename NativeMethodTraits::Type; + return JNINativeMethod{ name, TypeSignature()(), wrapper }; + } + }; + + protected: + Object self; + }; + + + + template < class T > + auto RemoveTag(const Binding& t) + { + return Object::Tag>(t).Get(); + } + + + template < class T > + struct TypeSignature< T, std::enable_if_t, T>::value> > { + const char * operator()() const + { + return TypeSignature::Tag>>()(); + } + }; + +} // jni diff --git a/include/jni/type_signature.hpp b/include/jni/type_signature.hpp index 090b95d..c5f91b1 100644 --- a/include/jni/type_signature.hpp +++ b/include/jni/type_signature.hpp @@ -9,7 +9,7 @@ namespace jni { - template < class > struct TypeSignature; + template < class, class = void > struct TypeSignature; template <> struct TypeSignature< jboolean > { const char * operator()() const { return "Z"; } }; template <> struct TypeSignature< jbyte > { const char * operator()() const { return "B"; } };