Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Even higher level bindings #29

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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

# --------------------------------------------------------------------------------------------------

Expand Down
17 changes: 17 additions & 0 deletions examples/Binding.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
103 changes: 103 additions & 0 deletions examples/binding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include "binding.hpp"

namespace java { namespace lang { struct Object; } }
namespace java { namespace lang { struct String; } }

namespace jni {

template <> struct Name<java::lang::Object> {
using Tag = ObjectTag;
};

template <> struct Name<java::lang::String> {
using Tag = StringTag;
};

} // namespace jni


namespace java {
namespace lang {

struct Object : jni::Binding<Object> {
using Binding::Binding;
};

struct String : jni::Binding<String> {
using Binding::Binding;

// Java methods

jni::jint length(jni::JNIEnv& env) {
static const auto method = jni_GetMethod<jni::jint()>(env, "length");
return self.Call(env, method);
}
};

} // namespace lang
} // namespace java



struct HighLevelBinding;

namespace jni {
template <> struct Name<HighLevelBinding>::Tag {
static constexpr auto Name() { return "HighLevelBinding"; }
};
} // namespace jni

struct HighLevelBinding : jni::Binding<HighLevelBinding> {
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<jni::jint(jni::jint)>(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<void(java::lang::String)>::Method<&HighLevelBinding::greet>("greet"),
jni_Bind<void(jni::jint)>::Method<&HighLevelBinding::greet>("greet"),
jni_Bind<void(jni::jdouble)>::StaticMethod<&HighLevelBinding::greet>("greet")
);
}
};


void HighLevelBinding::greet(jni::JNIEnv& env, java::lang::String args) {
const auto test = jni::Make<std::string>(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);
}
117 changes: 117 additions & 0 deletions examples/binding.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#pragma once

#include <jni/jni.hpp>

namespace jni
{

template < class T >
struct Name
{
struct Tag {};
};

template < class T >
class Binding
{
using Tag = typename Name<T>::Tag;

public:
// Constructs from jobject* pointers directly.
explicit Binding(UntaggedType<Object<Tag>> self_) : self(self_) {}

Binding(Object<Tag> self_) : self(self_) {}

static void jni_Register(jni::JNIEnv&) {}

operator Object<Tag>() const { return self; }

protected:
static auto jni_Class(JNIEnv& env)
{
static auto javaClass = Class<Tag>::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<Args...>(env);
return jni_Class(env).New(env, constructor, std::forward<Args>(args)...);
}

template < class Signature >
static auto jni_GetMethod(JNIEnv& env, const char* name)
{
return jni_Class(env).template GetMethod<Signature>(env, name);
}

template < class M >
struct jni_Bind;

template < class R, class... Args >
struct jni_Bind< R (Args...) > {
template <R (*method)(JNIEnv&, Args...)>
static auto StaticMethod(const char* name)
{
auto wrapper = [](JNIEnv* env, UntaggedType<Class<Tag>>, UntaggedType<Args>... args) -> UntaggedType<R>
{
try
{
return method(*env, AddTag<Args>(args)...);
}
catch (...)
{
ThrowJavaError(*env, std::current_exception());
return UntaggedType<R>();
}
};

using FunctionType = typename NativeMethodTraits<decltype(wrapper)>::Type;
return JNINativeMethod<FunctionType>{ name, TypeSignature<R (Args...)>()(), wrapper };
}

template <R (T::*method)(JNIEnv&, Args...)>
static auto Method(const char* name)
{
using Subject = Object<Tag>;
auto wrapper = [](JNIEnv* env, UntaggedType<Subject> subject, UntaggedType<Args>... args) -> UntaggedType<R>
{
try
{
return (T(AddTag<Subject>(*subject)).*method)(*env, AddTag<Args>(args)...);
}
catch (...)
{
ThrowJavaError(*env, std::current_exception());
return UntaggedType<R>();
}
};

using FunctionType = typename NativeMethodTraits<decltype(wrapper)>::Type;
return JNINativeMethod<FunctionType>{ name, TypeSignature<R(Args...)>()(), wrapper };
}
};

protected:
Object<Tag> self;
};



template < class T >
auto RemoveTag(const Binding<T>& t)
{
return Object<typename Name<T>::Tag>(t).Get();
}


template < class T >
struct TypeSignature< T, std::enable_if_t<std::is_base_of<Binding<T>, T>::value> > {
const char * operator()() const
{
return TypeSignature<Object<typename Name<T>::Tag>>()();
}
};

} // jni
2 changes: 1 addition & 1 deletion include/jni/array.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Object<TheTag>> New(JNIEnv& env, jsize length, const Class<TheTag>& clazz, const Object<TheTag>& initialElement = Object<TheTag>())
Expand Down
8 changes: 4 additions & 4 deletions include/jni/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ namespace jni
template < class... Args >
Object<TagType> New(JNIEnv& env, const Constructor<TagType, Args...>& method, const Args&... args) const
{
return Object<TagType>(&NewObject(env, *clazz, method, Untag(args)...));
return Object<TagType>(&NewObject(env, *clazz, method, RemoveTag(args)...));
}

template < class T >
Expand Down Expand Up @@ -85,20 +85,20 @@ namespace jni
auto Call(JNIEnv& env, const StaticMethod<TagType, R (Args...)>& method, const Args&... args) const
-> std::enable_if_t< IsPrimitive<R>::value, R >
{
return CallStaticMethod<R>(env, *clazz, method, Untag(args)...);
return CallStaticMethod<R>(env, *clazz, method, RemoveTag(args)...);
}

template < class R, class... Args >
auto Call(JNIEnv& env, const StaticMethod<TagType, R (Args...)>& method, const Args&... args) const
-> std::enable_if_t< !IsPrimitive<R>::value && !std::is_void<R>::value, R >
{
return R(reinterpret_cast<UntaggedType<R>>(CallStaticMethod<jobject*>(env, *clazz, method, Untag(args)...)));
return R(reinterpret_cast<UntaggedType<R>>(CallStaticMethod<jobject*>(env, *clazz, method, RemoveTag(args)...)));
}

template < class... Args >
void Call(JNIEnv& env, const StaticMethod<TagType, void (Args...)>& method, const Args&... args) const
{
CallStaticMethod<void>(env, *clazz, method, Untag(args)...);
CallStaticMethod<void>(env, *clazz, method, RemoveTag(args)...);
}

static Class Find(JNIEnv& env)
Expand Down
4 changes: 2 additions & 2 deletions include/jni/native_method.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ namespace jni

auto wrapper = [] (JNIEnv* env, UntaggedType<Subject> subject, UntaggedType<Args>... args) -> UntaggedType<R>
{
return method(*env, Tag<Subject>(*subject), Tag<Args>(args)...);
return method(*env, AddTag<Subject>(*subject), AddTag<Args>(args)...);
};

return MakeNativeMethod(name, TypeSignature<R (Args...)>()(), wrapper);
Expand All @@ -132,7 +132,7 @@ namespace jni
{
auto wrapper = [] (JNIEnv* env, UntaggedType<Subject> subject, UntaggedType<Args>... args) -> UntaggedType<R>
{
return method(*env, Tag<Subject>(*subject), Tag<Args>(args)...);
return method(*env, AddTag<Subject>(*subject), AddTag<Args>(args)...);
};

return MakeNativeMethod(name, TypeSignature<R (Args...)>()(), wrapper);
Expand Down
12 changes: 6 additions & 6 deletions include/jni/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,40 +106,40 @@ namespace jni
auto Call(JNIEnv& env, const Method<TagType, R (Args...)>& method, const Args&... args) const
-> std::enable_if_t< IsPrimitive<R>::value, R >
{
return CallMethod<R>(env, obj, method, Untag(args)...);
return CallMethod<R>(env, obj, method, RemoveTag(args)...);
}

template < class R, class... Args >
auto Call(JNIEnv& env, const Method<TagType, R (Args...)>& method, const Args&... args) const
-> std::enable_if_t< !IsPrimitive<R>::value && !std::is_void<R>::value, R >
{
return R(reinterpret_cast<UntaggedType<R>>(CallMethod<jobject*>(env, obj, method, Untag(args)...)));
return R(reinterpret_cast<UntaggedType<R>>(CallMethod<jobject*>(env, obj, method, RemoveTag(args)...)));
}

template < class... Args >
void Call(JNIEnv& env, const Method<TagType, void (Args...)>& method, const Args&... args) const
{
CallMethod<void>(env, obj, method, Untag(args)...);
CallMethod<void>(env, obj, method, RemoveTag(args)...);
}

template < class R, class... Args >
auto CallNonvirtual(JNIEnv& env, const Class<TagType>& clazz, const Method<TagType, R (Args...)>& method, const Args&... args) const
-> std::enable_if_t< IsPrimitive<R>::value, R >
{
return CallNonvirtualMethod<R>(env, obj, clazz, method, Untag(args)...);
return CallNonvirtualMethod<R>(env, obj, clazz, method, RemoveTag(args)...);
}

template < class R, class... Args >
auto CallNonvirtual(JNIEnv& env, const Class<TagType>& clazz, const Method<TagType, R (Args...)>& method, const Args&... args) const
-> std::enable_if_t< !IsPrimitive<R>::value, R >
{
return R(reinterpret_cast<UntaggedType<R>>(CallNonvirtualMethod<jobject*>(env, obj, clazz, method, Untag(args)...)));
return R(reinterpret_cast<UntaggedType<R>>(CallNonvirtualMethod<jobject*>(env, obj, clazz, method, RemoveTag(args)...)));
}

template < class... Args >
void CallNonvirtual(JNIEnv& env, const Class<TagType>& clazz, const Method<TagType, void (Args...)>& method, const Args&... args) const
{
CallNonvirtualMethod<void>(env, obj, clazz, method, Untag(args)...);
CallNonvirtualMethod<void>(env, obj, clazz, method, RemoveTag(args)...);
}

UniqueObject<TagType> NewGlobalRef(JNIEnv& env) const
Expand Down
Loading