user
user

Reputation: 5390

Callback/closure with JNR taking a pointer argument

I'm using JNR and trying to pass a callback function with the following C-equivalent signature:

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

into some C function. I have declared the callback nested in the JNR library interface on the Java side as:

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

with another function in the JNR library interface

public int doSomething(Fn fn);

to serve as a wrapper around doSomething in C code accepting int(*)(void const*, void const**, void**). But whenever I create a callback:

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

And pass it off to the doSomething method of my JNR library interface I get the runtime error:

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)

What's wrong with my use of Pointer?

Upvotes: 5

Views: 1242

Answers (2)

goto1134
goto1134

Reputation: 107

As wks said,

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.

As soon, as void const** and void** parameters are equivalent to array of unknown size, you should use jnr.ffi.byref.PointerByReference for these parameters as shown here:

public int call(Pointer a, PointerByReference b, PointerByReference c);

Then the usage of your method should be like this:

Pointer a = new Pointer.wrap(Runtime.getSystemRuntime(), variable);
PointerByReference b = new PointerByReference();
PointerByReference c = new PointerByReference();
call(a, b, c);
Pointer bValue = b.getValue();

To manage data further you should know exactly what kind of data you are dealing with.

Upvotes: 0

wks
wks

Reputation: 1148

If it is C calling Java, I don't think it is possible to give Java an actual Java array from barely a C pointer.

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.

I suggest doing pointer arithmetic by yourself in Java when accessing a pointer to an array. This requires the knowledge of platform-specific memory layout (e.g. the length of one pointer element in the array of pointers), but all native interfaces are platform-specific.

Upvotes: 0

Related Questions