I am developing some SWIG-generated Java bindings for a C library. The library contains functions that take parameters of type void *
. On the C side these would
The simplest solution is to use SWIG's <carrays.i> to make a type that wraps an array of float
and an array of int
and any other types you care about. These can be converted to SWIGTYPE_p_float
etc. trivially using the cast()
member function. The problem is that this can't automatically be converted to a SWIGTYPE_p_void
from within Java. You could in theory call:
new SWIGTYPE_p_void(FloatArray.getCPtr(myfloatarr));
but for various reasons (not least it's cumbersome) that's less than ideal. (It also has issues with memory ownership and garbage collection).
So instead I defined an interface:
public interface VoidPtr {
public long asVoidPtr();
}
That we can make the wrapped version of your library take as input and the array classes FloatArray
, IntArray
etc. implement for us.
This ends up with the module file:
%module test
%include <carrays.i>
%typemap(javainterfaces) FloatArray "VoidPtr"
%typemap(javainterfaces) IntArray "VoidPtr"
%typemap(javacode) FloatArray %{
public long asVoidPtr() {
return getCPtr(this);
}
%}
%typemap(javacode) IntArray %{
public long asVoidPtr() {
return getCPtr(this);
}
%}
%array_class(float, FloatArray);
%array_class(int, IntArray);
%typemap(jstype) void *arr "VoidPtr"
%typemap(javain) void *arr "$javainput.asVoidPtr()"
void foo(void *arr);
Which modifies void *arr
to be treated as our VoidPtr
type and automatically calls the asVoidPtr()
method. You could use typemap copying or macros to make this less repetitive. (Note, there's a possible issue with premature garbage collection that might need to be addressed here depending on how you planned to use this)
This allows us to write code like:
public class run {
public static void main(String[] argv) {
FloatArray arr = new FloatArray(100);
test.foo(arr);
}
}
I think this is the easiest, cleanest solution. There are several other ways you could solve this though:
It's also possible to write some code that would take an actual Java array rather than just the SWIG array_class
and implement this interface by calling a JNI function to obtain the underlying pointer. You'd have to write a version of this for every primitive type though, just like the above.
An interface file could then look something like:
%module test
%{
void foo(void *arr);
%}
%typemap(in,numinputs=0) JNIEnv *env "$1 = jenv;"
%rename(foo) fooFloat;
%rename(foo) fooInt;
%inline %{
void fooFloat(JNIEnv *env, jfloatArray arr) {
jboolean isCopy;
foo((*env)->GetFloatArrayElements(env, arr, &isCopy));
// Release after call with desired semantics
}
void fooInt(JNIEnv *env, jintArray arr) {
jboolean isCopy;
foo((*env)->GetIntArrayElements(env, arr, &isCopy));
// Release after call
}
%}
void foo(void *arr);
Which then gives you overloads of foo
which take float[]
and int[]
as well as SWIGTYPE_p_void
.
You could use a trick with a union:
%inline %{
union Bodge {
void *v;
float *f;
int *i;
};
%}
although this is considered bad form, it does generate you a Java interface that can be used to convert from SWIGTYPE_p_int
to SWIGTYPE_p_void
.
I think it's possible to make FloatArray
inherit from SWIGTYPE_p_void
, something like the following compiled but untested code:
%module test
%include <carrays.i>
%typemap(javabase) FloatArray "SWIGTYPE_p_void"
%typemap(javabody) FloatArray %{
private long swigCPtr; // Minor bodge to work around private variable in parent
private boolean swigCMemOwn;
public $javaclassname(long cPtr, boolean cMemoryOwn) {
super(cPtr, cMemoryOwn);
this.swigCPtr = SWIGTYPE_p_void.getCPtr(this);
swigCMemOwn = cMemoryOwn;
}
%}
%array_class(float, FloatArray);
void foo(void *arr);
This duplicates the pointer on the Java side, but nothing changes that (currently) in either the void pointer or array classes so that's not as big a problem as it first seems. (You could also make it protected in the base class with an alternative typemap I think, or use a modified version of carrays.i
that gets swigCPtr
via the getCPtr
function instead)
There's an alternative to this answer, it's very different and gives a more natural solution to this problem, closer to what you were looking for originally. The other suggestions were focused on adding overloads (tedious, manual) or making the array_class
es implement a common interface one way or another.
What it overlooks is that Object
is a good match for void*
in Java most of the time. Even arrays in Java are Objects. This means if you have SWIG map void*
to Object
it'll accept as inputs any arrays you might want to pass in. With a bit of care and some JNI we can then get a pointer to the start of that array to pass in to the function. Obviously we need to reject non array Object
s with an exception though.
We still end up writing some (private) helper functions to arrange extraction of the real underlying pointer and release it when done, but the nice thing about this solution is that we only have to do this once and then we end up with a typemap that can be used for any functions which take an array as void*
like this.
I ended up with the following SWIG interface for this solution:
%module test
%{
#include <stdint.h>
void foo(void *in) {
printf("%p, %d, %g\n", in, *(jint*)in, *(jdouble*)in);
}
%}
%typemap(in,numinputs=0) JNIEnv *env "$1 = jenv;"
%javamethodmodifiers arr2voidd "private";
%javamethodmodifiers arr2voidi "private";
%javamethodmodifiers freearrd "private";
%javamethodmodifiers freearri "private";
%inline %{
jlong arr2voidd(JNIEnv *env, jdoubleArray arr) {
void *ptr = (*env)->GetDoubleArrayElements(env, arr, NULL);
return (intptr_t)ptr;
}
void freearrd(JNIEnv *env, jdoubleArray arr, jlong map) {
void *ptr = 0;
ptr = *(void **)↦
(*env)->ReleaseDoubleArrayElements(env, arr, ptr, JNI_ABORT);
}
jlong arr2voidi(JNIEnv *env, jintArray arr) {
void *ptr = (*env)->GetIntArrayElements(env, arr, NULL);
return (intptr_t)ptr;
}
void freearri(JNIEnv *env, jintArray arr, jlong map) {
void *ptr = 0;
ptr = *(void **)↦
(*env)->ReleaseIntArrayElements(env, arr, ptr, JNI_ABORT);
}
%}
%pragma(java) modulecode=%{
private static long arrPtr(Object o) {
if (o instanceof double[]) {
return arr2voidd((double[])o);
}
else if (o instanceof int[]) {
return arr2voidi((int[])o);
}
throw new IllegalArgumentException();
}
private static void freeArrPtr(Object o, long addr) {
if (o instanceof double[]) {
freearrd((double[])o, addr);
return;
}
else if (o instanceof int[]) {
freearri((int[])o, addr);
return;
}
throw new IllegalArgumentException();
}
%}
%typemap(jstype) void *arr "Object"
%typemap(javain,pre=" long tmp$javainput = arrPtr($javainput);",post=" freeArrPtr($javainput, tmp$javainput);") void *arr "tmp$javainput"
void foo(void *arr);
This implements it for two array types, there's a small finite number and you could use fragments or macros to help with this too. Internally SWIG uses a jlong
to represent pointers. So for each array type we need a function that returns a pointer for a given array and another one to release it. These are private and part of the module class - nobody other than the module needs to know how this works.
There's then two functions which take the Object
and use instanceof
(ugly, but arrays in Java don't have any other common base or interface and generics don't help) and call the correct function to get/release the pointers.
With these then it's just two typemaps to set up SWIG to use it for all void *arr
arguments. The jstype typemap instructs SWIG to use Object
for void*
in these cases. The javain typemap arranges for a temporary local variable to hold the pointer (in a long
) and then for it to be used to make the call and to be cleaned up once the call has succeed or failed.