Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best JNI Pattern for wrapping C++ Objects?

I am working on a Java API where many of the Java objects are really wrappers for equivalent C++ objects. The Java objects create the C++ objects and are responsible for freeing them when they are no longer required. I am wondering about the best pattern to use for this, I can see two possible options:

  1. Construct the C++ object in the constructor using a static native method call and final variable to hold the native handle.

    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);
    }
    
  2. Construct the C++ object in the constructor using an instance native method call and a non-final variable to hold the native handle.

    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 am thinking that (1) is the better approach, because:

  • a. It means that I can set _nativeHandle to be immutable (final). So I don't need to worry about concurrent access to it or unexpected changes (the code is actually more complex that these simplistic examples).
  • b. Due to the constructor, I have formalised in the design, that any sub-class of NativeBackedObject is the owner of its respective native object (represented by _nativeHandle), as it cannot be constructed without it.

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

I could also see an alternative pattern to approach (1), let's call it approach (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 move back to a default constructor, which could help with creating mocks for testing etc. The major disadvantage though, is that I can no longer pass additional params to newFoo().

Perhaps there are other approaches that I have missed? Suggestions welcome...

like image 843
adamretter Avatar asked Jan 15 '16 15:01

adamretter


2 Answers

Have you tried SWIG (http://www.swig.org) which can generate Java wrappers of 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 documentation for SWIG says, consider simple test class:

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

the output for it is:

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);
  }

}
like image 111
Tomasz Jarosik Avatar answered Oct 30 '22 15:10

Tomasz Jarosik


The approach "By Call, Static" is indeed most efficient as per the benchmark at https://github.com/adamretter/jni-construction-benchmark but JavaCPP basically uses "By Call, Invoke" (which BTW can be made a bit more efficient by caching the jfieldID).

The reason why I chose to do it that way is to produce a cleaner Java interface file. The user can decide to either write it manually or let the tool generate it from header files. Either way, it ends up reading like a Java translation of some header files. However, the more cruft we add to that interface, the less it looks like C++, and the harder it becomes to write or read it. (That's one of the many things I don't like about SWIG.)

BTW, it's not forbidden to modify final variables from JNI, so that could be another reason why one might want to do it that way.

Of course, it's still possible to modify JavaCPP and support the more computationally efficient way of doing things, but we barely save any time at all that it hasn't proven to be an issue, yet.

like image 22
Samuel Audet Avatar answered Oct 30 '22 16:10

Samuel Audet