Skip to content

DicomJ/JNI-TL

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JNI-TL

C++ Java Native Interface Template Library

JNI-TL is a lightweight С++ library which facilitates using java objects from C++ code. It’s written on top of standard Java Native Inteface 6.0 API (comes with including <jni.h> header file) and merely comprises a few handy wrappers what makes using JNI API easier.
For further reading it’s expected that reader is familiar with technical aspects of JNI API.

Wrapping jclass and jobject

Basic wrappers for jclass and jobject types are represented by corresponding Class and Object C++ classes.

JNIEnv *jenv; jclass jcls; jobject jobj;

Class clazz(jenv, jcls);
Object object(clazz, jobj);

Accessing fields and methods

Object type inherits Class type and both of them leverage accessing Java object’s or/and class’s methods and fields. Objects of these classes basically act as maps of fields and methods which might be accessed by their definitions.

object[<method>](<arguments>)

<value> = object[<field>]
object[<field>] = <value>

the same for Class object

clazz[<method>](<arguments>)

<value> = clazz[<field>]
clazz[<field>] = <value>

Example:

jint value = object[Field<jint>("field_name")];
object[Field<jint>("field_name")] = value;

object[Method<jboolean>("equals", Args<jobject>("java.lang.Object")](object);

Fields and methods definitions

The Field definition is represented by the name and its type. And when jobject type is a case, object’s class name is must.

<Specifier>::Field<jtype>("<field_name>")
<Specifier>::Field<jobject>("<field_name>", "<fully.qualified.field.class.name>")

Like Field the Method definition requires both name and type of returned value as well as object’s class name when method returns jobject type.

<Specifier>::Method<returned-jtype>("<method_name>", [, <arguments>])
<Specifier>::Method<jobject>("<method_name>"[, <arguments>], "<fully.qualified.returned.class.name>")

In addition method’s arguments description must be defined whenever method has parameters to be passed to.
Args is a template with variadic count of template arguments. Each template argument’s type defines the corresponding parameter’s type passed to the method and all jobject parameters requires corresponding object’s class name to be passes into Args class constructor.

Args<[jtype|jobject,]...>([<fully.qualified.class.name.for.jobject.parameter>], ...)

There are several kind of <field>s and <method>s specified by prefix

Field type Description Applicable to
Class Object
Class::Static::Field Accessing class’s static fields yes yes
Object::Static::Field The same as Class::Static::Field yes yes
Object::Field Accessing object’s fields no yes
Method type Description Applicable to
Class Object
Class::Static::Method Accessing class’s static methods yes yes
Class::Method Accessing non-virtual object’s methods of specified class no yes
Object::Static::Method The same as Class::Static::Method yes yes
Object::Method Accessing object’s methods no yes

Wrapping jarray

Array template class wraps java built-in container object and is specified by type of array elements.

JNIEnv *jenv; jtypeArray jarr;

Array<jtype> array(jenv, jarr);

Access Array elements

There is difference in accessing elements of jobject type and elements of primitive types

  • accessing jobject elements
Array<jstring> array(...);

jstring si = array[i];
array[i] = si;

  • accessing all primitive elements
Array<jtype> array(...);
bool copyBack = false; # Tells whether copy all changes back to array

Array<jtype>::Elements elements = array.elements(copyBack);
jtype value = elements[i];
elements[i] = value;

elements.commit(); # Manually copy elements back to array

Writing elements back happens on Array::Elements destructor and only when elements have been acquired with a raised copyBack flag or might be fired manually by calling commit method on Elements object.

  • accessing all primitive elements with critical restrictions
Array<jtype>::Critical::Elements elements = array.critical(copyBack);
jtype value = elements[i];
elements[i] = value;

  • accessing some Region of primitive elements
    Requesting Region allows to access small amount of elements. An index counts elements in the range not from array beginning.
Array<jtype>::Region::Elements elements = array.region(Region(start, length), copyBack);
jtype value = elements[i];
elements[i] = value;

Wrapping jstring

String class allows to convert from jstring to std::string and vice versa.

JNIEnv *jenv; jstring jstr;

String string(jenv, jstr);
std::string stdstr = string;
jstr = String(jenv, "some native text");

Exception handling

// TBI

Using JNI-TL

  1. Add compilation of jni_tl.cpp file into an appropriate project
  2. #include <jni_tl.h> header file
  3. Whenever it’s possible declare using namespace JNI; since all classes are defined under this namespace.

Best practice

Performance optimization

Accessing Fields or Methods by subscript operator of Class or Object comprises two calls of JNI API functions:

  1. GetFieldID/GetStaticFieldID/GetMethodID/GetStaticMethodID
  2. Get<jtype>Field/Set<jtype>Field/CallStatic<jtype>Method/Call<jtype>Method/CallNonvirtual<jtype>Method

Example:

jenv->Set<jtype>Field(object, jenv->GetFieldID(clazz, "field_name", "field_signature"), <value>);

Suppose the field is accessed several times

for (int i = 0; i < N; ++i) {
  jenv->Set<jtype>Field(object, jenv->GetFieldID(clazz, "field_name", "field_signature"), <value>);
}

In this case code might be optimized

jfieldID fieldID = jenv->GetFieldID(clazz, "field_name", "field_signature");
for (int i = 0; i < N; ++i) {
  jenv->Set<jtype>Field(object, fieldID, <value>);
}

In the similar way might be optimized accesing to Fields and Methods

Object::Field<jtype>::ID fieldID = clazz(Object::Field<jint>("field_name"));
for (int i = 0; i < N; ++i) {
  object[fieldID] = <value>;
}

Wrapping Java object

The best way access Java object of some class is through a corresponding wrapper which implements necessary functionality and hides JNI specifics. In the most cases wrapper merely inherits JNI::Object and provides a bunch of necessary functions.
The next example shows how to make wrapper for a part of java.lang.Integer java class and than use it.

#include "jni_tl.h"

using namespace JNI;

struct Integer : Object {

  Integer(const Env &env, jobject object = 0)
    : Object(Class("java.lang.Integer"), object) {

    MIN = (*this)[Static::Field<jint>("MIN_VALUE")];
    MAX = (*this)[Static::Field<jint>("MAX_VALUE")];

    hashCodeID = (*this)(Method<jint>("hashCode"));
    bitCountID = (*this)(Static::Method<jint>("bitCount", Args<jint>()));
  }

  jint hashCode() const {
    return (*this)[hashCodeID]();
  }

  jint bitCount(jint value) const {
    return (*this)[bitCountID](value);
  }

  jint max() const { return MAX; }
  jint min() const { return MIN; }

  private: jint MAX, MIN;

  private: Method<jint>::ID hashCodeID;
  private: Static::Method<jint>::ID bitCountID;
};

void UsingIntegerCodeSnippet(JNIEnv *jenv, jobject jinteger) {
  Integer integer(jenv, jinteger);
  jint bits = integer.bitCount(0xF);
  jint hash = integer.hashCode();
  jint max = integer.max();
  jint min = integer.min();
}

About

C++ Java Native Interface Template Library

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published