Обратный вызов / закрытие с JNR, принимающим аргумент указателя
Я использую JNR и пытаюсь передать функцию обратного вызова со следующей C-эквивалентной сигнатурой:
int fn(void const*, void const**, void**)
в некоторую функцию C. Я объявил обратный вызов, вложенный в интерфейс библиотеки JNR на стороне Java, как:
public static interface Fn {
@Delegate public int call(Pointer a, Pointer[] b, Pointer[] c);
}
с другой функцией в интерфейсе библиотеки JNR
public int doSomething(Fn fn);
служить оберткой doSomething
в коде C принимает int(*)(void const*, void const**, void**)
, Но всякий раз, когда я создаю обратный вызов:
new Fn() { int call() { ... } };
И передать это doSomething
метод моего интерфейса библиотеки JNR я получаю ошибку времени выполнения:
java.lang.ExceptionInInitializerError
Caused by:
java.lang.IllegalArgumentException: unsupported closure parameter type class [Ljnr.ffi.Pointer;
at jnr.ffi.provider.jffi.NativeClosureProxy.newProxyFactory(NativeClosureProxy.java:109)
at jnr.ffi.provider.jffi.NativeClosureFactory.newClosureFactory(NativeClosureFactory.java:84)
at jnr.ffi.provider.jffi.NativeClosureManager.initClosureFactory(NativeClosureManager.java:71)
at jnr.ffi.provider.jffi.NativeClosureManager.getClosureFactory(NativeClosureManager.java:49)
at jnr.ffi.provider.jffi.NativeClosureManager.newClosureSite(NativeClosureManager.java:81)
at jnr.ffi.provider.jffi.InvokerTypeMapper.getToNativeConverter(InvokerTypeMapper.java:68)
at jnr.ffi.provider.jffi.InvokerTypeMapper.getToNativeType(InvokerTypeMapper.java:143)
at jnr.ffi.mapper.CachingTypeMapper.lookupAndCacheToNativeType(CachingTypeMapper.java:71)
at jnr.ffi.mapper.CachingTypeMapper.getToNativeType(CachingTypeMapper.java:43)
at jnr.ffi.mapper.CompositeTypeMapper.getToNativeType(CompositeTypeMapper.java:34)
at jnr.ffi.provider.jffi.InvokerUtil.getParameterTypes(InvokerUtil.java:185)
at jnr.ffi.provider.jffi.AsmLibraryLoader.generateInterfaceImpl(AsmLibraryLoader.java:125)
at jnr.ffi.provider.jffi.AsmLibraryLoader.loadLibrary(AsmLibraryLoader.java:59)
at jnr.ffi.provider.jffi.NativeLibraryLoader.loadLibrary(NativeLibraryLoader.java:43)
at jnr.ffi.LibraryLoader.load(LibraryLoader.java:265)
at jnr.ffi.LibraryLoader.load(LibraryLoader.java:244)
Что не так с моим использованием Pointer
?
2 ответа
Как сказал wks,
Во-первых, объект массива Java имеет встроенную длину, но само значение указателя C не содержит верхней границы. В C обычно используются соглашения, такие как строки с нулевым символом в конце или передача двух аргументов (ptr, len).
Во-вторых, массив Java должен быть заполнен перед использованием (то есть не ленивым). Это подразумевает загрузку всех указателей в массив, что может быть неэффективно. Это также необходимо сделать с известной длиной массива.
Как только void const**
а также void**
параметры эквивалентны массиву неизвестного размера, вы должны использовать jnr.ffi.byref.PointerByReference
для этих параметров, как показано здесь:
public int call(Pointer a, PointerByReference b, PointerByReference c);
Тогда использование вашего метода должно быть таким:
Pointer a = new Pointer.wrap(Runtime.getSystemRuntime(), variable);
PointerByReference b = new PointerByReference();
PointerByReference c = new PointerByReference();
call(a, b, c);
Pointer bValue = b.getValue();
Для дальнейшего управления данными вы должны точно знать, с какими данными вы имеете дело.
Если это C, вызывающая Java, я не думаю, что можно дать Java фактический массив Java только из указателя на C.
Во-первых, объект массива Java имеет встроенную длину, но само значение указателя C не содержит верхней границы. В C обычно используются соглашения, такие как строки с нулевым символом в конце или передача двух аргументов (ptr, len).
Во-вторых, массив Java должен быть заполнен перед использованием (то есть не ленивым). Это подразумевает загрузку всех указателей в массив, что может быть неэффективно. Это также необходимо сделать с известной длиной массива.
Я предлагаю выполнять арифметику указателей самостоятельно в Java при обращении к указателю на массив. Это требует знания структуры памяти для конкретной платформы (например, длины одного элемента указателя в массиве указателей), но все нативные интерфейсы зависят от платформы.