The main thing you're looking for here are two features of typemaps. Firstly you want multi argument typemaps to be able to work with arguments that are logically grouped. Secondly you want argout typemaps to have a chance at converting arguments into outputs.
There are a few different ways to write such interfaces inside SWIG, with the main trade-offs covering quantity of C (JNI) vs actual Java that needs to be written. I've put together one complete example:
%module test
%{
#include <assert.h>
%}
%typemap(in,numinputs=0) (DataStruct **ppdata, size_t *psize) (size_t tempsize, DataStruct *tempdata) %{
$2 = &tempsize;
$1 = &tempdata;
%}
%typemap(jtype) int get_all_data "long[]";
%typemap(jstype) int get_all_data "DataStruct[]";
%typemap(javaout) int get_all_data {
final long[] arr = $jnicall;
DataStruct[] ret = new DataStruct[arr.length];
for (int i = 0; i < arr.length; ++i) {
ret[i] = new DataStruct(arr[i], false);
}
return ret;
}
%typemap(jni) int get_all_data "jlongArray";
%typemap(out) int get_all_data %{
// Handle errors in the int return value ($1)
assert(!$1);
%}
%typemap(argout) (DataStruct **ppdata, size_t *psize) {
$result = JCALL1(NewLongArray, jenv, *$2);
jlong temparr[*$2];
for (size_t i = 0; i < *$2; ++i) {
*(DataStruct**)&temparr[i] = &((*$1)[i]);
}
JCALL4(SetLongArrayRegion, jenv, $result, 0, *$2, temparr);
}
%inline %{
typedef struct {
int id;
double x;
double y;
} DataStruct;
int get_all_data ( long ref, DataStruct **ppdata, size_t *psize ) {
static const size_t size = 4;
*ppdata = (DataStruct*) malloc(sizeof(DataStruct) * size);
*psize = size;
for (size_t i = 0; i < size; ++i) {
DataStruct val = {i,i,i};
(*ppdata)[i] = val;
}
return 0;
}
%}
This does a few things:
- Converts the return type of
get_all_data to be an array. Internally it's an array of longs that represent pointers to DataStruct instances but the visible part of it is an array of DataStruct objects that get constructed.
- The input typemap creates temporaries to be used as placeholders for the output arguments.
- Once the call actually happens we loop over the array and store the addresses of the members into the Java array. This is important because the array is an array of objects, not an array of pointers. SWIG's generated proxy objects function in relation to pointers to individual objects so we need to get those from somewhere. We can't compute them inside Java either because exposing the
sizeof information needed to do so correctly from just the raw output pointer is somewhat tricky so this is a good compromise. (It also means the same code could work if you switched to e.g. linked lists in the future with only small changes).
With that in place I can run my test:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
DataStruct[] result = test.get_all_data(0);
for (int i = 0; i < result.length; ++i) {
System.out.println(result[i].getId());
}
}
}
This runs as:
swig2.0 -Wall -java -c++ test.i
gcc -Wall -Wextra -shared -o libtest.so -I/usr/lib/jvm/default-java/include -I/usr/lib/jvm/default-java/include/linux test_wrap.cxx
javac run.java
LD_LIBRARY_PATH=. java run
0
1
2
3