JNR 采用指针参数的回调/闭包

Posted

技术标签:

【中文标题】JNR 采用指针参数的回调/闭包【英文标题】:Callback/closure with JNR taking a pointer argument 【发布时间】:2014-10-05 04:02:37 【问题描述】:

我正在使用 JNR 并尝试使用以下 C 等效签名传递回调函数:

int fn(void const*, void const**, void**)

进入一些 C 函数。我已将嵌套在 Java 端的 JNR 库接口中的回调声明为:

public static interface Fn 
  @Delegate public int call(Pointer a, Pointer[] b, Pointer[] c);

与 JNR 库接口中的另一个函数

public int doSomething(Fn fn);

在接受int(*)(void const*, void const**, void**) 的C 代码中充当doSomething 的包装器。但是每当我创建回调时:

new Fn()  int call()  ...  ;

并将其传递给我的 JNR 库接口的 doSomething 方法,我得到运行时错误:

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 有什么问题?

【问题讨论】:

发现这个:github.com/jnr/jnr-ffi/blob/master/src/main/java/jnr/ffi/… 不管什么原因,Pointer 数组在某个时候被支持,然后被注释掉了。或者它们是一个想法并在实施之前被注释掉?还是什么? 无论如何,目前正在废弃[]s,只是通过get方法通过指针参数访问指针元素... 【参考方案1】:

如果是 C 调用 Java,我认为不可能只用一个 C 指针给 Java 一个实际的 Java 数组。

首先,Java 数组对象具有内置长度,但 C 指针值本身不包含上限。 C 通常使用诸如以零结尾的字符串或传递两个参数(ptr、len)之类的约定。

其次,必须在使用之前填充 Java 数组(即不懒惰)。这意味着加载所有指向数组的指针,这可能效率低下。这也需要使用已知的数组长度来完成。

我建议在访问指向数组的指针时自己在 Java 中进行指针运算。这需要了解特定于平台的内存布局(例如,指针数组中一个指针元素的长度),但所有本机接口都是特定于平台的。

【讨论】:

【参考方案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();

要进一步管理数据,您应该确切地知道您正在处理的数据类型。

【讨论】:

以上是关于JNR 采用指针参数的回调/闭包的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript闭包和回调详解

Java内部类之间的闭包和回调详解

回调函数参数问题,闭包解决方案示例

回调函数和闭包

是否可以使用接收数据作为参数的函数的闭包范围?

利用闭包向post回调函数传参数