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 typically be passed as a pointer to an array of type float
or int
cast to void *
. In the generated Java bindings, this results in methods that take parameters of type SWIGTYPE_p_void
.
What is the best way to construct an array of floats/ints in the Java bindings so that they can be passed as type SWIGTYPE_p_void
to these methods?
At the moment I am defining a helper function in my example.i file:
void *floata_to_voidp(float f[])
{
return (void *)f;
}
And then on the Java side doing something like this:
float foo[] = new float[2];
SWIGTYPE_p_void av = null;
// do something with foo
av = example.floata_to_voidp(foo);
example.myfunction(av);
This seems rather ugly, especially as I would also need an inta_to_voidp()
etc in my SWIG interface file for each type conversion I want to support.
Is there a way to do this without helper functions and involving less extra code on the Java side to convert data types?
UPDATE (17/6/12): to give additional detail to the question: what I'm trying to do is take a set of C functions, with prototype int foo(const float *data, int N, const void *argv, float *result)
and map them to methods on the Java side where an array of arbitrary type can be passed in as argv
. Note that argv
is const void *
and not void *
.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With