tags:

views:

539

answers:

3

The SWIG documentation explains how a variety of input types in C, like this:

void spam1(Foo *x);      // Pass by pointer
void spam2(Foo &x);      // Pass by reference
void spam3(Foo x);       // Pass by value
void spam4(Foo x[]);     // Array of objects

... would all take a single type of argument in Java, like this:

Foo f = new Foo();  // Create a Foo
example.spam1(f);   // Ok. Pointer
example.spam2(f);   // Ok. Reference
example.spam3(f);   // Ok. Value.
example.spam4(f);   // Ok. Array (1 element)

Similarly, for return types in C:

Foo *spam5();
Foo &spam6();
Foo  spam7();

... all three functions will return a pointer to some Foo object that will be assigned to a Java object variable, the final one requiring an allocation of a value type that the Java garbage collection will take care of upon release.

But suppose spam5() returns a pointer to an array. In Java, I have to use array semantics to access the individual elements, but I don't think that I can just do this:

Foo foo[] = spam5();

I don't even think the compiler would accept a cast to (Foo[]), so how does this work in SWIG?

+2  A: 

This problem does not have a simple or automatic solution. Believe me, I looked.

The problem is that SWIG doesn't know how big the array you're returning is supposed to be, so it can't generate a Java array. You can't supply the size as an argument to the function, either (gross as that would be) - typemaps don't work that way.

In the general case you have to write another wrapper function, which takes a C array and a length as an out parameter, and use a typemap to turn those two parameters into a Java array. Or, if you don't mind using carrays.i, you can skip the second step and just work with C arrays directly from Java.

David Seiler
Great. I look forward to hearing more.
Buggieboy
A: 

In java an array is an Object, So, if spam5() return Object, then the compiler will allow you to cast that to an array of Foo. This is valid Java:

    class Bar {
        static class Foo {}
        Foo[] foo = {new Foo(), new Foo()};
        Object o = foo;
        // ...
        Foo[] bar = (Foo[])o;
    }
Devon_C_Miller
Yes, but if you use SWIG to bind to a C function returning Foo*, SWIG will think the function is returning a single Foo and generate a wrapper function that returns a Foo, not a Foo[].
David Seiler
I think that David has put his finger on the dilemma.
Buggieboy
Please see this:http://stackoverflow.com/questions/1522660/java-and-sdlgetkeystatefor a situation in sdljava where this occurs. SWIGTYPE_p_unsigned_char is returned. It's an object reference, which my Java compiler doesn't seem to want to turn into an array.
Buggieboy
A: 

you can always use directly JNI :

imagine you want to retrieve an xyz vector array

public class demo{
  public native vecteur[] returnArray();

  ....
}

in the swig wrap cxx file add the func that will fill the java vect array

JNIEXPORT jobjectArray JNICALL 
               Java_demo_returnArray
  (JNIEnv *env, jobject jobj){

    jobjectArray ret;
    int i;
    jclass objClass;
    jmethodID mid;
    jobject myobj;
    jmethodID setX;
   objClass = env->FindClass("vect");
   if (!objClass)
   {
      printf("class not found\n");
      exit(0);
   }
    ret= (jobjectArray)env->NewObjectArray(5, // change with the size of your array or a variable 
         objClass,
         env->NewStringUTF(""));//FIXME
    // call javap -s myclass to know the names
    mid=env->GetMethodID( objClass, "<init>", "()V"); // looking for the vect class constructor
    if (!mid)
      {
        printf("vect() not found\n");
        exit(0);
      }


    for(i=0;i<5;i++) {
         myobj=env->NewObject( objClass, mid); // myobj = new vect()

         // get vect::setX method
         setX=env->GetMethodID( objClass, "setX", "(F)V"); // looking for vect::setX method
         if(!setX)
           {
             printf("method vect::setX not found\n");
             exit(0);
           }
         // call setX method with param i
         env->CallVoidMethod(myobj, setX,(float)i); // change i with your array value
         env->SetObjectArrayElement(
                                   ret,i,myobj);
        }
    return(ret);
  }

and finally main class

// main.java
public class main {
  static {
    System.loadLibrary("myclass");
  }

  public static void main(String argv[]) {
      demo l = new demo();
      vecteur f[] = l.returnArray();    
      System.out.println("array size : "+f.length);
      for (int i = 0;i < f.length;i++)
          System.out.println(f[i].getX());

  }
}
stéphane ancelot