1

I'm having issues passing in an appropriate Java data type for the fetchByType C function's length parameter from my Java implementation (both below). On the C side, the *length parameter is a pointer to a length variable (output) to indicate the size of the data to be returned from the fetchByType C function. I've tried to use the SWIG %apply int32_t { int32_t * } and pass an int initialized at 0 from my Java implementation but that fails. I've also tried passing in a SWIGTYPE_p_int as shown below, but that also doesn't work. I don't have an exception unfortunately as this just crashes.

C Function:

void * fetchByType(struct row_t *result_row, type_t attribute, int32_t *length);

Generated Java from SWIG:

public static String fetchByType(SWIGTYPE_p_result_row_t result_row, type_t attribute, SWIGTYPE_p_int length)

Java Implementation:

SWIGTYPE_p_int length = new SWIGTYPE_p_int();
fetchByType(result_row, attribute, length)
c12
  • 9,557
  • 48
  • 157
  • 253

1 Answers1

3

The problem here is that you need to pass something that can be changed into the C function. That means you can't use a primitive in Java (e.g. int) since Java always passes primitives by value.

There's only really one sensible way you can work around this. SWIG in fact already provides a few typemap we can use that passes this as an array instead. Arrays in Java are "objects" and so int[] is passed by reference even though int isn't. A simplified example based on the function you show above, first test.h:

inline void fetchByType(int32_t *length) {
  *length = 123;
}

And a module file, test.i:

%module test

%{
#include "test.h"
%}

// knows about things like int *OUTPUT:
%include "typemaps.i"
// knows about int32_t
%include "stdint.i"
// let's have an int32_t *OUTPUT for our int32_t *lenght
%apply int32_t *OUTPUT { int32_t *length }

%include "test.h"

This just makes sure we apply the appropriate *OUTPUT typemap. (stdint.i maps this onto a real C primitive for you). I tested with:

public class run {
  public static void main(String[] argv) {
    int arr[] = new int[1];
    test.fetchByType(arr);
    System.out.println(arr[0]);
  }
}

It's a bit of a hack1, but it's usable. Another approach might be to use a java.lang.Integer, which also represents an int and is a proper Object so gets passed by reference. The problem there is that java.lange.Integer is immutable however and I'm not actually sure enough how that works on the JNI side to provide an example of this.

1 Other languages SWIG supports alter the return type for some typemaps and return a tuple or similar for OUTPUT arguments, but that's not really feasible with Java.

Community
  • 1
  • 1
Flexo
  • 87,323
  • 22
  • 191
  • 272
  • I was able to use your example, great solution. I tried to use an Integer but then on the Java side you can only pass an Integer as null or with a default value (Integer length = new Integer(0)) but neither way worked. The Integer length=null actually threw a null pointer which makes sense. Is it good practice to include any .i files right after the %module declaration? I moved the typemaps.i and the stdint.i there and my generated code seems ok...any downside to that? – c12 Nov 30 '11 at 22:44
  • @c12 - I have a strong suspicion that the other approach I mentioned using `Integer` instead is actually not possible in Java at all, at least not without altering the return type which defeats the point. As for the `%include` - it doesn't matter much where you put them, so long as they're before the first place they're needed. The `%{ #include "test.h" %}` just tells SWIG to pass `#include "test.h"` straight to the generated wrapper, so it really doesn't make any difference which is first there. My habit is to stick anything that needs to be `#include`d at the top and then follow w/`%include` – Flexo Nov 30 '11 at 22:50