Home

Awesome

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

Rules for Types

Rules for Parameters and Results

Rules for Error Handling

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:

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:

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.

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:

Prior art