2016-01-15 16 views
6

Java nesnelerinin çoğunun, eşdeğer C++ nesneleri için gerçekten sarmalayıcı olduğu bir Java API'sı üzerinde çalışıyorum. Java nesneleri C++ nesneleri oluşturur ve artık gerekmediklerinde onları serbest bırakmaktan sorumludur. Bunun için kullanılacak en iyi desen hakkında merak ediyorum, ben iki olası seçenekleri görebilirsiniz:C++ Nesneleri sarma için en iyi JNI Kalıbı?

  1. yerli kolu tutmak için statik bir yerel yöntem çağrısı ve son değişken kullanmak yapıcısındaki C++ nesne Construct.

    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. doğal kolu tutmak için bir örneği, doğal yöntem çağrısı olmayan bir son değişken kullanarak yapıcısında C++ nesnesi oluşturun.
    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); 
    } 
    
  3. anda

O (1) daha iyi bir yaklaşımdır düşünüyorum

, çünkü:

  • a. Bu, _nativeHandle'u immutable olarak ayarlayabileceğim anlamına gelir (final). Dolayısıyla, eşzamanlı erişim veya beklenmedik değişiklikler hakkında endişelenmem gerekmiyor (kod, bu basit örneklerin aslında daha karmaşıktır).
  • b. Yapıcıdan dolayı, NativeBackedObject'un herhangi bir alt sınıfının (_nativeHandle tarafından temsil edilen) kendi yerel nesnesinin sahibi olmadığı için tasarımda resmileştirildiğini ve bu sayede oluşturulmadığını resmileştirdim.

Yaklaşma (1) üzerinde (1) veya yaklaşımla ilgili herhangi bir sorun (1) var mıdır?

Ben de yaklaşmak için alternatif desen görebiliyordu

(1), yaklaşımı diyelim (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); 
} 

avantajı (3) (1), ben geri hareket olmasıdır üzerinde Testler için alay yaratmaya yardımcı olabilecek bir varsayılan kurucu. Büyük dezavantaj da, artık ek paramları newFoo()'a geçiremem.

Belki de kaçırdığım başka yaklaşımlar var mı? Öneriler hoş geldiniz ...

+0

NativeBackedObjects ne tür bir yaşam döngüsüne sahip olur? –

+0

Yaşam döngüsü, API tüketicisi tarafından manuel olarak yönetilecek, AutoCloseable özelliğini kullanabildim, böylece temizliği yönetmek için kaynakları kullanmayı deneyin. – adamretter

+0

Herhangi bir multithreading yapacak mısınız? Ayrıca '_nativeOwner' AtomicBoolean'ınızı anlamaya çalışıyorum. Bu nesnenin yapıcısı tarafından oluşturulan "NativeBackedObject" adlı bir 'final' örneği değişkeni verildiğinde, sahiplik olarak tanımladığınız şeyi değiştiren ek kodunuz yoksa, bu Boolean için herhangi bir gereksinim görmüyorum. Mevcut “NativeBackedObject” öğesinin **, yerel kodunuzda "yeni" yoluyla aldığım, kendi yerel nesnesine ** referansı var. Mülkiyeti geçmek zorundasın, ve eğer mümkün olursa, bundan kaçınmalıyım. O & M kabusu olurdu. –

cevap

1

C++ nesnelerinin Java sarmalayıcılarını oluşturabilen SWIG (http://www.swig.org) denediniz mi?

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

bunun için çıkışı:

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

} 
+0

Hayır. Ne tür bir desen izlediğini biliyor musun? – adamretter

+0

Teşekkürler tomasz-jarosik çok ilginç. Öyle görünüyor ki, SWIG burada belgelediğim kalıplardan "By Call" yapıyor - https://github.com/adamretter/jni-construction-benchmark – adamretter

+1

Harika karşılaştırma! Bence SWIG her yerde statik olduğu için "By Call, static" (Statik olarak) kullanıyor. –

1

"Arama, Statik By" yaklaşımı gerçekten en verimli olduğu yudum diyor belgelerine olarak

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

, basit bir test sınıfı düşünün referans olarak https://github.com/adamretter/jni-construction-benchmark ancak JavaCPP10 temel olarak "Arayan, Çağır" seçeneğini kullanır (BTW, biraz jfieldID'u önbelleğe alarak verimli şekilde aktarın.

Bu şekilde yapmayı tercih etmemizin nedeni, daha temiz bir Java arabirim dosyası oluşturmaktır. Kullanıcı el ile yazmayı veya aracın üstbilgi dosyalarından oluşturmasına izin verebilir.Her iki şekilde de, bazı başlık dosyalarının bir Java çevirisi gibi okumayı bitirir. Ancak, bu ara yüze daha fazla çarptık eklediğimizde, C++ gibi gözüküyor ve yazmayı ya da okumayı zorlaştırıyor. (Bu, SWIG hakkında pek sevmediğim birçok şeyden biri.)

BTW, JNI'den final değişkenlerini değiştirmek yasak değildir, bu yüzden bu şekilde yapmak isteyebilecek başka bir neden olabilir.

Tabi ki, JavaCPP'yi modifiye etmek ve işleri daha verimli bir şekilde yapmanın daha etkin bir yolunu bulmak hala mümkündür, ancak henüz bir sorun olduğu kanıtlanmadığı için zamandan tasarruf etmekteyiz.

İlgili konular