Best JNI template for wrapping C ++ objects?

I am working on a Java API where many Java objects are indeed wrappers for equivalent C ++ objects. Java objects create C ++ objects and are responsible for releasing them when they are no longer needed. I am wondering how best to use this template, I see two possible options:

  • Build a C ++ object in the constructor using a call to the static native method and a final variable to hold the built-in descriptor.

    public abstract class NativeBackedObject1 implements java.lang.AutoCloseable { protected final long _nativeHandle; protected final AtomicBoolean _nativeOwner; protected NativeBackedObject1(final long nativeHandle) { this._nativeHandle = nativeHandle; this._nativeOwner = new AtomicBoolean(true); } @Override public close() { if(_nativeOwner.copareAndSet(true, false)) { disposeInternal(); } } protected abstract void disposeInternal(); } public SomeFoo1 extends NativeBackendObject1 { public SomeFoo1() { super(newFoo()); } @Override protected final void disposeInternal() { //TODO: any local object specific cleanup disposeInternal(_nativeHandle); } private native static long newFoo(); private native disposeInternal(final long nativeHandle); } 
  • Build a C ++ object in the constructor using a call to the native native method, and not a finite variable to store the built-in descriptor.

     public abstract class NativeBackedObject2 implements java.lang.AutoCloseable { protected long _nativeHandle; protected boolean _nativeOwner; protected NativeBackedObject2() { this._nativeHandle = 0; this._nativeOwner = true; } @Override public void close() { synchronized(this) { if(_nativeOwner && _nativeHandle != 0) { disposeInternal(); _nativeHandle = 0; _nativeOwner = false; } } } protected abstract void disposeInternal(); } public SomeFoo2 extends NativeBackendObject2 { public SomeFoo2() { super(); _nativeHandle = newFoo(); } @Override protected final void disposeInternal() { //TODO: any local object specific cleanup disposeInternal(_nativeHandle); } private native long newFoo(); private native disposeInternal(final long nativeHandle); } 

At the moment, I think that (1) is a better approach because:

  • a. This means that I can set _nativeHandle unchanged ( final ). So I don’t need to worry about concurrent access to it or about unexpected changes (the code is actually more complex than these simplified examples).
  • b. Due to the constructor, I formalized in the design that any subclass of the NativeBackedObject class is the owner of its corresponding native object (represented by _nativeHandle ), since it cannot be built without it.

Are there any advantages of approach (2) to (1) or any problems with approach (1)?

I could also see an alternative pattern for approach (1), let it fit (3):

 public abstract class NativeBackedObject3 implements java.lang.AutoCloseable { protected final long _nativeHandle; protected final AtomicBoolean _nativeOwner; protected NativeBackedObject3() { this._nativeHandle = newInternal(); this._nativeOwner = new AtomicBoolean(true); } @Override public close() { if(_nativeOwner.copareAndSet(true, false)) { disposeInternal(); } } protected abstract long newInternal(); protected abstract void disposeInternal(); } public SomeFoo3 extends NativeBackendObject3 { public SomeFoo3() { super(); } @Override protected final void disposeInternal() { //TODO: any local object specific cleanup disposeInternal(_nativeHandle); } @Override protected long newInternal() { return newFoo(); }; private native long newFoo(); private native disposeInternal(final long nativeHandle); } 

The advantage of (3) over (1) is that I can return to the default constructor, which can help in creating layouts for testing, etc. The main drawback, however, is that I can no longer pass additional parameters to newFoo() .

Perhaps there are other approaches that I skipped? Suggestions are welcome ...

+6
source share
2 answers

Have you tried SWIG ( http://www.swig.org ), which can generate Java wrappers for C ++ objects?

 %typemap(javabody) SWIGTYPE %{ private long swigCPtr; protected boolean swigCMemOwn; public $javaclassname(long cPtr, boolean cMemoryOwn) { swigCMemOwn = cMemoryOwn; swigCPtr = cPtr; } public static long getCPtr($javaclassname obj) { return (obj == null) ? 0 : obj.swigCPtr; } %} 

As the documentation for SWIG says, consider a simple testing class:

 class Test { string str; public: Test() : str("initial") {} }; 

output for him:

 public class Test { private long swigCPtr; protected boolean swigCMemOwn; protected Test(long cPtr, boolean cMemoryOwn) { swigCMemOwn = cMemoryOwn; swigCPtr = cPtr; } protected static long getCPtr(Test obj) { return (obj == null) ? 0 : obj.swigCPtr; } protected void finalize() { delete(); } // Call C++ destructor public synchronized void delete() { if(swigCPtr != 0 && swigCMemOwn) { swigCMemOwn = false; exampleJNI.delete_Test(swigCPtr); } swigCPtr = 0; } // Call C++ constructor public Test() { this(exampleJNI.new_Test(), true); } } 
+1
source

The “By Call, Static” approach is really the most effective according to the benchmark in https://github.com/adamretter/jni-construction-benchmark but JavaCPP mainly uses “By Call, Invoke” (which BTW can be made a bit more efficient, caching jfieldID ).

The reason I decided to do this is to create a cleaner Java interface file. The user can decide to either record it manually or let the tool generate it from the header files. In any case, it ends up reading like a Java translation of some header files. However, the steeper we add to this interface, the less it looks like C ++, and the more difficult it becomes to write and read. (This is one of many things that I don't like about SWIG.)

By the way, it is not forbidden to modify final variables from JNI, so this may be another reason why this could be done.

Of course, you can still modify JavaCPP and support a more computationally efficient way of doing something, but we hardly save any time until it is a problem.

+1
source

All Articles