Я работаю над Java API, где многие из объектов Java действительно являются обертками для эквивалентных объектов C++. Объекты Java создают объекты C++ и несут ответственность за освобождение их, когда они больше не требуются. Я задаюсь вопросом о наилучшем шаблон, чтобы использовать для этого, я вижу два возможных варианта:Лучший шаблон JNI для обертывания объектов C++?
Построить C++ объект в конструкторе, используя статический нативный вызов метода и конечную переменную для хранения родной ручки.
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); }
Построить C++ объекта в конструкторе экземпляр с помощью вызова метода нативного и неконечного переменного для хранения родной ручки.
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); }
На данный момент я имею в виду, что (1) является лучшим подходом, потому что:
- а. Это означает, что я могу установить
_nativeHandle
неизменным (final
). Поэтому мне не нужно беспокоиться о параллельном доступе к нему или о неожиданных изменениях (код на самом деле более сложный, чем эти упрощенные примеры). - b. Из-за конструктора я формализовал в дизайне, что любой подкласс класса
NativeBackedObject
является владельцем его соответствующего нативного объекта (представленный_nativeHandle
), так как он не может быть построен без него.
Есть ли какие-либо преимущества подхода (2) по сравнению с (1) или любые проблемы с подходом (1)?
Я мог бы также увидеть альтернативный шаблон подойти (1), давайте назовем его подход (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);
}
Преимущество (3) по (1), является то, что я могу вернуться к конструктор по умолчанию, который может помочь в создании макетов для тестирования и т. д. Основным недостатком является то, что я больше не могу передавать дополнительные параметры newFoo()
.
Возможно, есть другие подходы, которые я пропустил? Предложения приветствуются ...
Какой жизненный цикл будут иметь ваши NativeBackedObjects? –
Жизненный цикл будет вручную управляться потребителем API, я реализовал AutoCloseable, поэтому у них есть возможность использовать try-with-resources для управления очисткой. – adamretter
Вы собираетесь делать многопоточность? Я также пытаюсь вычислить ваш '_nativeOwner' AtomicBoolean. Учитывая, что это «конечная» переменная экземпляра «NativeBackedObject», созданная конструктором этого объекта, если у вас нет дополнительного кода, который не отображается, который изменяет то, что вы определяете как право собственности, я не вижу необходимости в этом булевом. В текущем 'NativeBackedObject' есть ** ссылка ** на ваш собственный объект, полученный, как я предполагаю, с помощью' new' в вашем собственном коде. Вам нужно будет передать владение, и я бы избегал этого, если это вообще возможно. Это был бы кошмар O & M. –