2014-10-05 5 views
4

Я использую JNR и пытается передать функцию обратного вызова с помощью следующего C-эквивалента подписи:Обратного вызова/замыкание JNR принимает аргумент указателя

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?

+0

Нашел: https://github.com/jnr/jnr-ffi/blob/master/src/main/java/jnr/ffi/provider/jffi/NativeClosureProxy.java#L227 по любой причине, массивы указателя были поддержаны в какой-то момент, а затем прокомментированы. Или они представляют собой идею и закомментированы до тех пор, пока не будут реализованы? Или что-то? – user

+0

В любом случае, в настоящее время отказывается от '[]' s и просто получает доступ к элементам указателя через аргументы указателя с помощью методов 'get' ... – user

ответ

0

Если это C-вызов Java, я не думаю, что можно дать Java реальный массив Java с едва указателем C.

Во-первых, объект массива Java имеет встроенную длину, но само значение C-указателя не содержит верхнюю границу. C обычно используют условные обозначения, такие как строки с нулевым завершением, или передают два аргумента (ptr, len).

Во-вторых, перед использованием используется массив Java, т. Е. Не ленивый. Это подразумевает загрузку всех указателей на массив, что может быть неэффективным. Это также необходимо сделать с известной длиной массива.

Я предлагаю сделать арифметику указателя самостоятельно в Java при доступе к указателю на массив. Для этого требуется знание макета памяти на платформе (например, длина одного элемента указателя в массиве указателей), но все собственные интерфейсы являются специфичными для платформы.

0

Как wks сказал

Firstly, a Java array object has a built-in length, but a C pointer value itself does not contain the upper-bound. C usually use conventions like zero-terminated strings, or passing two arguments (ptr, len).

Secondly, a Java array has to be populated before using (i.e. not lazy). This implies loading all pointers to the array, which may be inefficient. This also needs to be done with a known array length.

Как только, так как 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(); 

Для дальнейшего управления данными вы должны точно знать, с какими данными вы имеете дело.