Giter Site home page Giter Site logo

jni.hpp's Introduction

jni.hpp is a modern, type-safe, header-only, C++14 wrapper for JNI (Java Native Interface). Its aim is to make calling Java from C++, or C++ from Java, convenient and safe, without sacrificing low-level control, as you commonly do with code generation approaches.

Two levels of wrappers are provided.

Low-level wrappers

The low-level wrappers match JNI, type-for-type and function-for-function, with modern C++ equivalents in a jni namespace. The matching uniformly follows a set of rules.

Rules for Names

  • Everything in jni.hpp is in the jni namespace.
  • When a name exists in JNI, the same name is used in the namespace.
  • When the original name is a macro, a lowercase name is used instead, e.g. jni::jni_ok for JNI_OK.

Rules for Types

  • Types corresponding to Java primitive types (jni::jboolean, jni::jint, jni::jdouble, etc.) are matched unchanged.
  • Types corresponding to Java reference types (jni::jobject, jni::jclass, jni::jarray, etc.) are non-copyable and non-constructible. Their references and pointers automatically convert to base types as expected. (These types do not hide pointer-ness behind a confusing typedef, like the underlying JNI headers do.)
  • jni::jsize is std::size_t, not a signed integer. jni.hpp checks for overflow in the necessary places.
  • Ownership types are instantiations of unique_ptr, and using declarations are provided for each specific instantiation, e.g. jni::UniqueGlobalRef.
  • Families of functions, such as Call*Method and CallStatic*Method, are matched with a single template function, such as jni::CallMethod<ResultType> and jni::CallStaticMethod<ResultType>.
  • jni::jvalue and the Call*MethodV and Call*MethodA function families are made redundant by type safety, and omitted.

Rules for Parameters and Results

  • Parameters are passed, and results returned, by value or reference, not by pointer. An exception is made for possibly-null jni::jobject parameters and results, which are pointers.
  • When transferring ownership out of a function, the return type is an ownership type.
  • When transferring ownership into a function, the parameter type is an ownership type.
  • Output parameters are returned, not passed as pointer-to-pointer. When there are multiple outputs, they are returned as a std::tuple.
  • Whenever a function receives paired "pointer to T and length" parameters, an overload is provided that accepts a statically-sized array, std::array<T>, std::vector<T>, or (if T is a character type) std::basic_string<T> argument. These overloads compute the length automatically.
  • In string-related functions, char16_t replaces jchar.

Rules for Error Handling

  • Errors are thrown, not returned or otherwise left to be explicitly checked.
  • Pending Java exceptions are checked after every JNI call that may produce a pending Java exception, and thrown as jni::PendingJavaException.
  • Invocation API errors are thrown as std::system_errors containing std::error_codes with a custom category.

High-level wrappers

The high-level wrappers provide additional syntactic convenience and type safety when working with non-primitive Java types. They are built around the concept of class tags. A class tag is a C++ type that provides a static Name method returning a fully-qualified Java class name, thus associating a unique C++ type with the corresponding Java class. For example:

struct Math { static constexpr auto Name() { return "java/lang/Math"; } };

The high-level wrappers consist of a set of classes templated on class tag:

  • jni::Class<Tag>, a wrapper for a reference to the Java class associated with the tag.
  • jni::Object<Tag>, a wrapper for a (possibly-null) reference to an instance of the Java class associated with the tag.
  • jni::Array<E>, a wrapper for a (possibly-null) reference to a Java array. The element type E is a jni.hpp primitive type or Object<Tag>.
  • jni::Constructor<Tag, Args...>, a wrapper for a constructor for the the Java class associated with the tag. The result type R and each argument type in Args is a jni.hpp primitive type or Object<Tag>.
  • jni::Method<Tag, R (Args...)>, a wrapper for an instance method of the Java class associated with the tag. The result type R and each argument type in Args is a jni.hpp primitive type or Object<Tag>.
  • jni::StaticMethod<Tag, R (Args...)>, a wrapper for a static method of the Java class associated with the tag. The result type R and each argument type in Args is a jni.hpp primitive type or Object<Tag>.
  • jni::Field<Tag, T>, a wrapper for an instance field of the Java class associated with the tag, and providing Get and Set methods. The field type T is a jni.hpp primitive type or Object<Tag>.
  • jni::StaticField<Tag, T>, a wrapper for a static field of the Java class associated with the tag, and providing Get and Set methods. The field type T is a jni.hpp primitive type or Object<Tag>.

Method Registration

Registering native methods is a central part of JNI, and jni.hpp provides several features that make this task safer and more convenient. The jni.hpp wrapper method jni::RegisterNatives has the following signature:

template < class... Methods >
void RegisterNatives(jni::JNIEnv& env, jni::jclass&, const Methods&... methods);

In other words, rather than receiving a length and pointer to an array of JNIMethods, it takes a variable number of variable types. This allows jni::RegisterNatives to type check the methods, ensuring that their type signatures are valid for JNI.

Use the helper function jni::MakeNativeMethod to construct method arguments for jni::RegisterNatives. jni::MakeNativeMethod wraps your method in a try / catch block that translates C++ exceptions to Java exceptions. It is overloaded on the following combinations of arguments:

  • A const char * name, const char * signature, and lambda. The type of the first parameter of the lambda must be jni::JNIEnv&. The type of the second parameter must be either jni::jclass* (for a native static method) or jni::jobject* (for a native instance method). The result type must be a JNI primitive type or type convertable to jni::jobject*. (These requirements are type checked.)
  • A const char * name, const char * signature, and function pointer. The function has the same parameter and return type requirements as the lambda. In order to guarantee a unique exception-handling wrapper for each unique function pointer, the function pointer must be provided as a template parameter rather than method parameter:
jni::MakeNativeMethod<decltype(myFunction), myFunction>(name, signature)

The repetition of myFunction with decltype is necessary because it is not possible to infer the function type from the non-type template parameter. You may wish to define and use a macro to avoid the repetition.

  • A const char * name and lamba whose parameter and return types use high-level jni.hpp wrapper types. In this case, jni.hpp will compute the signature automatically.
  • A const char * name and function pointer whose parameter and return types use high-level jni.hpp wrapper types. Again, jni.hpp will compute the signature automatically, and again, the function pointer must be provided as a template parameter rather than method parameter.

Finally, jni.hpp provides a mechanism for registering 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. Between creation and finalization, a pointer to the native peer is stored in a long field on the Java object. jni.hpp will take care of wrapping lambdas, function pointers, or member function pointers with code that automatically gets the value of this field, casts it to a pointer to the peer, and calls the member function (or passes a reference to the peer as an argument to the lambda or function pointer). See the example code for details.

Example code

Example code for both the low-level and high-level wrappers is provided in the examples subdirectory. This code shows the use of jni.hpp for:

  • Registering native methods such that they can be called from Java.
  • Calling back into Java methods from native methods.
  • Native peer registration.

Prior art

  • Many code generation approaches. SWIG, JavaCPP, and so on. But jni.hpp is explicitly not a binding generator.
  • JniHelpers is similar in spirit. In comparison, jni.hpp takes advantage of modern C++ features such as unique_ptr and variadic templates, introduces no preprocessor macros, and is header-only -- no build step required.
  • I hear that Boost.Python is similar, but I did not reference it in building this library.
  • The low-level wrapping strategy is inspired by Lisa Lippincott's presentation "How to call C libraries from C++" from Cppcon 2014, and by her previous work "The Nitrogen Manifesto". However jni.hpp does not use "PlusPlus", the wrapping library abstraction mentioned in the presentation; I found it to be a poor economy compared to simply writing out the wrappers manually.

jni.hpp's People

Contributors

daedric avatar ivovandongen avatar jcelerier avatar jfirebaugh avatar kiryldz avatar kkaefer avatar manaswinidas avatar spinorx avatar tobrun avatar xezon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jni.hpp's Issues

Build error

When just including jni.hpp, with g++-6, I get an error at :

template < class R, class Subject, class... Args, R (*method)(JNIEnv&, Subject, Args...) >
struct NativeMethodMaker< R (JNIEnv&, Subject, Args...), method >

The error :

‘R(struct _JNIEnv&, Subject, Args ...)’ is not a valid type for a template non-type parameter
     struct NativeMethodMaker< R (JNIEnv&, Subject, Args...), method >
                                                                      ^

non-throwing New{Local,Global}Ref for weak refs

The way you promote a weak ref to a strong one is by calling NewLocalRef or NewGlobalRef, which will return null if the input has been GC'd. But jni.hpp's existing bindings of NewLocalRef and NewGlobalRef will throw in this case. It needs overloads of these functions for const UniqueWeakGlobalRef<T>& and const Weak<T>& that return null rather than throwing.

Questions about returning objects and using function objects

I have a C++ API that looks like this:

class device
{
public:
	virtual ~device();

	virtual void disconnect() = 0;

	virtual int read(void *dest, int num_bytes) = 0;
	virtual int write(void const *src, int num_bytes) = 0;
};

typedef std::function<void(int xvalue, std::array<std::uint8_t, 8> address)> details_callback;

class dev_manager
{
public:
    void start(std::string name, details_callback details);
    void stop();
    
    std::unique_ptr<device> connect(std::array<std::uint8_t, 8> address);
};

I am trying to use jni.hpp to create bindings to Java/Kotlin.

So far, it worked well. I use jni::RegisterNativePeer to create bindings to a "DevManager" Kotlin class.

The parts that are unclear so far though are:

  1. What do I do about connect() ? It returns a unique_ptr to an abstract base class. In the Kotlin class definition, I'd have something like fun connect(address: ByteArray): Device. I currently have no idea how to properly wrap the return value here. One old-school method would be to pass the pointer as a long to the Kotlin code, and have it call native functions that accept that long as an argument. But that's old-school C style, as said. Can jni.hpp do this in a more modern C++ manner?
  2. How could the function object that is passed to start() be translated to Java/Kotlin?

Oh, also, currently, I manually use jni::Arrayjni::jbyte to read the address (since the address in Java/Kotlin is present as a byte array). So, I have code like this:

address to_address(jni::JNIEnv &env, jni::Array<jni::jbyte> const &byte_array)
{
	jni::jsize array_size = byte_array.Length(env);
	if (array_size != address().size())
		throw exception("Invalid address bytearray size");

	address addr;

	for (jni::jsize i = 0; i < array_size; ++i)
		addr[i] = byte_array.Get(env, i);

	return addr;
}

However, I get the impression that jni.hpp could handle this automatically, that is, it can implement its own std::array<-> JNI array wrapper. Or did I misunderstand something?

How can I use the library to expose a native method taking std::string?

Or otherwise said, do I expect of the library to "wrap"/generate marshalling code (and if so, to which extent?), or should I declare my native methods to take something as close as possible to the Java types? In this case, jstring. But that fails like:

/mnt/data/dev/mapbox-jnihpp/include/jni/tagging.hpp:130:40:   required by substitution of ‘template<class T> using UntaggedType = decltype (jni::Untag(declval<T>())) [with T = jni::jstring]’
/mnt/data/dev/mapbox-jnihpp/include/jni/tagging.hpp:124:64: error: ‘const struct jni::jstring’ has no member named ‘get’

Questions

  • Why is jni::Class a standalone type rather than a class that inherits from jni::Object with a tag of java/lang/Class<T>?
  • Should we add a a static cache to Class::Find?
  • jni.hpp has an ObjectTag, but doesn't expose any of the Java functions on the Object object. are other object supposed to inherit from jni::ObjectTag?

Native method signatures

2 questions:
(1) You say "The type of the first parameter of the lambda must be jni::JNIEnv&. The type of the second parameter must be either jni::jclass*". Yet, in your Calculator example there in only the Env: "jni::jlong Add(jni::JNIEnv&, jni::jlong a, jni::jlong b)". Why?
(2) Why would you require the native class to take these 1 or 2 extra parameters? Isn't the purpose of your lib to present an already-defined class in a form consumable from Java?

How do I convert a jobject to jni::Object?

Considering the following code:

import android.app.Activity;

class NativeClassProxy {
  private long peer;
  private Activity mActivity;

  public void initialize(Activity activity) {
    mActivity = activity;
  }
}
struct Activity { static constexpr auto Name() { return "android/app/Activity"; } };
struct NativeClassProxy { static constexpr auto Name() { return "NativeClassProxy"; } };

NativeClass::NativeClass() {
  auto env = cocos2d::JniHelper::getEnv();
  auto cls = jni::Class<NativeClassProxy>::Find(*env);
  auto constructor = cls.GetConstructor(*env);
  auto object = cls.New(*env, constructor);

  auto initialize = cls.GetMethod<void (jni::Object<Activity>)>(*env, "initialize");

  jobject activity = cocos2d::JniHelper::getActivity();
  object.Call(*env, initialize, activity);
}

Of course, I can't send the activity directly since it expects a jni::Object<Activity>. But how do I convert it into what it expects?

Static methods registration

Hi! At first, thank you for creating this library, it happened to be very useful for my applications.

I have a question regarding static method registration. Tests https://github.com/mapbox/jni.hpp/blob/master/test/high_level.cpp#L869 seem to imply that this is a correct way of registering a static native method. However, I found out in a rather hard way that:

  1. That method is static, but in a sense that it's a static member function of a class, but... it takes the explicit reference to "this" as an argument, which, in my opinion kind of defeats the purpose of a static method. Am I missing something here?

  2. Tests also indicate that the following definition https://github.com/mapbox/jni.hpp/blob/master/test/high_level.cpp#L13 is how a truly static method shall be defined, but honestly, there doesn't seem to be a way to register it via RegisterNativePeer. And that makes sense, since RegisterNativePeer looks like something used to associate class methods with class instance.

Still though, ideally, I wanted to have a static native method like this:

struct X {
    static constexpr auto Name() { ... };
    
    static jni::int method(jni::JNIEnv&, jni::Class<X>&) { return 0; }
};

and some corresponding Java class:

public class X {
    static native int method();
}

that is possible to register using some of the jni.hpp APIs. Is that somehow possible? Or should I just go ahead and do it the regular way (creating a symbol with appropriate name so that the JVM can call it directly)?

Incorrect type signature code

Hi!

The code in

template < class R, class... Args >
struct TypeSignature< R (Args...) >
{
private:
template < class... T > void DoNothingWith( T&&... ) const {}
std::string Compute() const
{
static std::string result("(");
DoNothingWith( ( result += TypeSignature<Args>()() )... );
result += ")";
result += TypeSignature<R>()();
return result;
}
is incorrect.

It passes argument pack expansion as arguments to the function DoNothingWith(). Per standard compiler is free to arrange function arguments computation in any order - and gcc and clang disagree here. Clang computes arguments left to right, producing correct signature; android gcc 4.9 computes them right to left, producing completely invalid function signature.

You can verify it here

Typing for generics

Even though Java generics are erased before runtime and not exposed via JNI, it might be useful to have them integrated in jni.hpp somehow for additional type-safety. One potential starting point: provide a way to write a method binding that specifies specialized generic types such as Object<List<Object<Integer>>>.

Improve ergonomics of calling Java from C++

To call a Java method from C++, you currently must do quite a lot. As an example, consider Number::floatValue. You must:

  • Define class Float with a Name method returning "java/lang/Float"
  • Declare and initialize a jni::Class<Float>
  • Call GetMethod<jni::jfloat ()>(env, "floatValue") on it
  • Once you have an Object<Number> instance, do number.Call(env, method)

Ideally, you'd be able to just do number.floatValue(). Is there any way we can get closer to that?

Refs mapbox/mapbox-gl-native#8809

Attempt to remove non-JNI local reference, dumping thread

We have heard a couple of reports downstream in gl-native related to the following log message:

Attempt to remove non-JNI local reference, dumping thread

Capturing from a Chromium issue:

Whenever the parameters passed from Java to a JNI method (i.e. the incoming jobject parameters) are wrapped in a ScopedLocalJavaRef, this causes them to be deleted once they go out of scope with JNIEnv::DeleteLocalRef. On recent versions of ART with CheckJNI turned on, this causes a spammy warning to be printed to logcat stating "Attempt to remove non-JNI local reference, dumping thread" with a thread dump, as apparently parameters are not supposed to be deleted, only objects returned as local references from native->java JNI calls. This is not actually a problem since the runtime just does nothing in this case (other than printing the warning)

Discussion and related commits can be found in chromium/506850.

Should high-level bindings always seize local refs?

In mapbox-gl-native, we're essentially manually seizing local refs whenever they are generated, in order to ensure that the local reference table never overflows. Djinni does the same thing.

Should the high-level bindings just always return jni::Local<T>s?

[BUG] jni::Class/Object::Call() blindly passes arguments to JNI's variable length argument methods

Passing int to a method that takes jlong likely leads to random stack memory being accessed and thus random trash being passed around.

For example, this is the problematic code:

struct Duration {
    static constexpr auto Name() {
        return "java/time/Duration";
    }
};

auto clazz = jni::Class<Duration>::Find(env);
auto of_seconds = clazz.template GetStaticMethod<jni::Object<Duration>(jni::jlong, jni::jlong)>(env, "ofSeconds");
auto duration = clazz.Call(env, of_seconds, 0, 0); // note the integers rather than jlongs here

auto get_seconds = clazz.template GetMethod<jni::jlong()>(env, "getSeconds");
auto seconds = duration.Call(env, get_seconds); // which is now some random value

Of course jni::Class::Call has some compile-time checks for type convertability, but that's simply not enough, because, some stacks are aligned to 4 bytes, and some are aligned to 8 bytes, and when the alignment is less than sizeof(jlong) things start to get pretty messed up. At least that's the reason of this behavior in my opinion.

Simple fix to the code above is to replace:

auto duration = clazz.Call(env, of_seconds, 0, 0); // note the integers rather than jlongs here

with:

auto duration = clazz.Call(env, of_seconds, jni::jlong{0}, jni::jlong{0});

But that's definitely not ideal. As a temporary workaround I've applied the following patch to class.hpp, which seem to compile and do whatever I wanted (in this specific scenario, but this doesn't solve the problem globally as there are many other Call like functions), but don't know if that's actually the best possible solution:

diff --git a/include/jni/class.hpp b/include/jni/class.hpp
index 6cf96c2..48460a1 100644
--- a/include/jni/class.hpp
+++ b/include/jni/class.hpp
@@ -74,7 +74,7 @@ namespace jni
                -> std::enable_if_t< IsPrimitive<R>::value
                                  && Conjunction<std::is_convertible<const ActualArgs&, const ExpectedArgs&>...>::value, R >
                {
-                return CallStaticMethod<R>(env, *this->get(), method, Untag(args)...);
+                return CallStaticMethod<R>(env, *this->get(), method, Untag(static_cast<const ExpectedArgs&>(args))...);
                }
 
             template < class R, class... ExpectedArgs, class... ActualArgs >
@@ -83,14 +83,14 @@ namespace jni
                                  && !std::is_void<R>::value
                                  && Conjunction<std::is_convertible<const ActualArgs&, const ExpectedArgs&>...>::value, Local<R> >
                {
-                return Local<R>(env, reinterpret_cast<typename R::UntaggedType*>(CallStaticMethod<jobject*>(env, *this->get(), method, Untag(args)...)));
+                return Local<R>(env, reinterpret_cast<typename R::UntaggedType*>(CallStaticMethod<jobject*>(env, *this->get(), method, Untag(static_cast<const ExpectedArgs&>(args))...)));
                }
 
             template < class... ExpectedArgs, class... ActualArgs >
             auto Call(JNIEnv& env, const StaticMethod<TagType, void (ExpectedArgs...)>& method, const ActualArgs&... args) const
                -> std::enable_if_t< Conjunction<std::is_convertible<const ActualArgs&, const ExpectedArgs&>...>::value >
                {
-                CallStaticMethod<void>(env, *this->get(), method, Untag(args)...);
+                CallStaticMethod<void>(env, *this->get(), method, Untag(static_cast<const ExpectedArgs&>(args))...);
                }
 
             static Local<Class> Find(JNIEnv& env)

Anyway, I'll try to make it into pull request some time if nobody's gonna do it before me. ;-)

Some warnings on g++

On g++ 7.2.1

In file included from jni.hpp/include/jni/functions.hpp:4:0,
                 from jni.hpp/include/jni/jni.hpp:6,
jni.hpp/include/jni/errors.hpp: In member function ‘virtual std::__cxx11::string jni::ErrorCategory()::Impl::message(int) const’:
jni.hpp/include/jni/errors.hpp:33:25: warning: case value ‘-3’ not in enumerated type ‘jni::error’ [-Wswitch]
                         case jni_eversion:  return "Version error";
                         ^~~~
jni.hpp/include/jni/errors.hpp:32:25: warning: case value ‘-2’ not in enumerated type ‘jni::error’ [-Wswitch]
                         case jni_edetached: return "Detached error";
                         ^~~~
jni.hpp/include/jni/errors.hpp:31:25: warning: case value ‘-1’ not in enumerated type ‘jni::error’ [-Wswitch]
                         case jni_err:       return "Unspecified error";
                         ^~~~
jni.hpp/include/jni/errors.hpp:30:25: warning: case value ‘0’ not in enumerated type ‘jni::error’ [-Wswitch]
                         case jni_ok:        return "OK";
                         ^~~~

Cast's arguments are in the wrong order

It should be Cast(env, clazz, object), not Cast(env, object, clazz).

  • When composing a pipeline of method calls, you want the subject last
  • Matches the order of appearance in other cast operations (static_cast, etc.)

Fix compilation with -Wdouble-promotion

Currently if -Wdouble-promotion is enabled, then invoking methods that take float parameters will trigger the warning. For example:

    jni::Method<Tag, jni::jfloat> method = klass.GetMethod<void, jni::float>(...);
    object.Call(env, method, 0.0f);

The warning looks something like:

.../jni.hpp/include/jni/functions.hpp:202:73: error: implicit conversion increases floating-point precision: 'float' to 'double' [-Werror,-Wdouble-promotion]
           Wrap<jobject*>(env.NewObject(Unwrap(clazz), Unwrap(method), Unwrap(std::forward<Args>(args))...)));
                              ~~~~~~~~~                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../jni.hpp/include/jni/class.hpp:50:41: note: in instantiation of function template specialization 'jni::NewObject<signed char, signed char, short, short, int, int, long long, long long, float, double, unsigned char, jni::jstring *>' requested here
               return Object<TagType>(&NewObject(env, *clazz, method, Untag(args)...));

This is because float is automatically promoted to double in varargs.

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48379 notes that this is a pretty useless case for this warning, but still, we could suppress it by introducing a templated Vararg function that does an explicit static cast in the case of float.

jni::Object<> doesn't automatically cast from derived to base class

A jni::Object<> to a derived class does not lend itself to be easily cast down to a jni::Object<> to its base class:

jni::Object<Derived> derived;
jni::Object<Base> base = child; // no viable conversion!

Compiler error:

-error: no viable conversion from 'Object<mbgl::android::VectorSource>' to 'Object<mbgl::android::Source>'

The workarounds require using jni::Cast() or using the raw pointer to create a new jni::Object<>:

jni::Object<Derived> child;

jni::Object<Base> base = jni::Cast(env, derived, Base::javaClass);
    OR
jni::Object<Base> base = jni::Object<Base>(derived.Get())

Null value

Is it possible to pass/receive null-value form C to java and from java to C?
Can't find a good way to make it by jni

Is there a way to add more method to native peer

jni::RegisterNativePeer<Calculator>(env, jni::Class<Calculator>::Find(env), "peer",
    std::make_unique<Calculator, JNIEnv&>,
    "initialize",
    "finalize",
    METHOD(&Calculator::Add, "add"),
    METHOD(&Calculator::Subtract, "subtract"));

needs all method mapping to be specified in RegisterNativePeer. Is there anyway to just specify constructor and initializer with this and add/attach methods one by one?

Why:
I have quite a few methods and the method is becoming long. Also if there is error in signature mismatch its hard to trace which among these have the issue.

Was looking for something like:
ini::RegisterNativeMethod(
env, jni::Class::Find(env),
METHOD(&Calculator::Add, "add"));

Incorrect wrapping code

The code at
native_method:328 seems incorrect, why would you do std::decay_t&?

Comment mine:
auto wrapper = [field, initializer] (JNIEnv& e, Object& obj, /std::decay_t&/Args... args) // TODO why reference?

This causes wrapping of constructors to fail.

High-level bindings should have more implicit derived-to-base conversions

#26 was a good start, but we should go further:

  • Object<T>::Set(env, field, value) should allow an implicit derived-to-base conversion from value's type (derived) to the type of the field (base).
  • Object<T>::Call(env, method, arg) should allow an implicit derived-to-base argument from arg's type (derived) to the corresponding argument type of the method (base).
  • ...and so on

Right now, template parameters constrain these types to be identical. For example, if you try Call a method that's expecting a Object<> parameter, and pass a jni::String, you get an error "no matching member function for call to 'Call'", "candidate template ignored: deduced conflicting types for parameter 'Args' (<jni::Object<jni::ObjectTag>> vs. <jni::Object<jni::StringTag>>)".

Add java.lang.ref.WeakReference wrapper

JNI weak global references are somewhat unsafe -- a weak global reference may be promoted to a strong reference even during finalization of that object, leading to potential use-after-free errors.

WeakReference has more reliable semantics, and should be preferred in all downstream cases I know of that currently use jni::Weak or jni::UniqueWeakGlobalRef.

To make this convenient, jni.hpp should add an ownership type that acts similarly to jni::Weak but uses a global reference to a WeakReference internally.

This is what djinni does as well:

https://github.com/dropbox/djinni/blob/a38f5ee8b5bc5e8b4b04628d63b221294b6a986c/support-lib/jni/djinni_support.hpp#L303-L305

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.