Skip to content

Commit

Permalink
test even higher level bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
kkaefer committed Jan 3, 2018
1 parent 316f8ef commit 05c8211
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 2 deletions.
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/type_signature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"; } };
Expand Down

0 comments on commit 05c8211

Please sign in to comment.