Search code examples
c++java-native-interface

How can I return vector<vector<float>> from JNI?


I have a function in C++:

std::vector<std::vector<float>> const &GetVertices() { return m_Vertices; }

I need to return this value to Java through JNI.

So, because of the fact that I need to return vector of vector, I think that I have to use jobjectArray, like this:

extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_google_ar_core_examples_
java_helloar_HelloArActivity_fillListWithData(
    JNIEnv *env,
    jobject /* this */
) 

In Java, I have this method:

public native Object[] fillListWithData();

So, my question is, how to convert vector<vector<float>> to jobjectArray?

I know that there is a method that could create jobjectArray:

jobjectArray verticesArr = env->NewObjectArray(verticesVec.size(), WHAT CLASS SHOULD BE HERE?,NULL);

Then how can I put in the values ?

Full class implementation

extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_google_ar_core_examples_java_
helloar_HelloArActivity_fillListWithData(
    JNIEnv *env,
    jobject /* this */
) {
    //verticesVec
    vector<vector<float>> verticesVec = initializer->GetVertices(); // THIS VECTOR I NEED TO CONVERT TO JOBJECTARRAY
    jobjectArray verticesArr = env->NewObjectArray(verticesVec.size(), WHAT CLASS SHOULD BE HERE?,NULL);

    //HOW TO FILL THE ARRAY HERE??

    return verticesArr;
}

Solution

  • You have to play with java.util.Vector. This way, you can make a very simple mapping of

    (C++ side) vector<vector<float> >  --->  Vector<Vector<Float>> (Java side)
    

    The code itself will be little bit ugly. Remember that playing with JNI is not quite pleasant experience (due to esoteric syntax).

    Anyway, what you want to do is to create all the stuff on C++ side and pass it back to Java.

    Just an excerpt

      vector<vector<float> > vect {
                                     { 1.1, 1.2, 1.3 },
                                     { 2.1, 2.2, 2.3 },
                                     { 3.1, 3.2, 3.3 }
                                  };
      ...
      ...
    
      jclass vectorClass = env->FindClass("java/util/Vector");
      ...
    
      jclass floatClass = env->FindClass("java/lang/Float");
      ...
    
      jmethodID mid = env->GetMethodID(vectorClass, "<init>", "()V");
      jmethodID addMethodID = env->GetMethodID(vectorClass, "add", "(Ljava/lang/Object;)Z");
    
      // Outer vector
      jobject outerVector = env->NewObject(vectorClass, mid);
      ...
    
      for(vector<float> i : vect) {
    
        // Inner vector
        jobject innerVector = env->NewObject(vectorClass, mid);
    
        for(float f : i) {
          jmethodID floatConstructorID = env->GetMethodID(floatClass, "<init>", "(F)V");
          ...
    
          // Now, we have object created by Float(f)
          jobject floatValue = env->NewObject(floatClass, floatConstructorID, f);
          ...
    
          env->CallBooleanMethod(innerVector, addMethodID, floatValue);
        }
    
        env->CallBooleanMethod(outerVector, addMethodID, innerVector);
    
      }
    
      env->DeleteLocalRef(vectorClass);
      env->DeleteLocalRef(floatClass);
    

    You can find full sample code here:

    https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo045

    Once you run the test, you can see that C++ based data was passed to Java

    > make test
    /Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/bin/java -Djava.library.path=:./lib -cp target recipeNo045.VectorOfVectors
    library: :./lib
    [1.1,1.2,1.3]
    [2.1,2.2,2.3]
    [3.1,3.2,3.3]