Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass array(array of long in java) from Java to C++ using Swig

Tags:

java

c++

swig

I have sample .h file like below:

class Test
{
public:
       void SelectValues(long long values[])
};

I used SWIG and created JNI interface from below .i file

%module MyLib
%include "carrays.i"
%array_functions(long long, long_long_array )


%{
  #include "Test.h"
%}

/* Let's just grab the original header file here */
%include <windows.i> /*This line is used for calling conventions*/ 
% include "Test.h"

When I create Java method it creates like:

public void SelectValues(SWIGTYPE_p_long_long includeKeys)

Also for JNI file it is taking argument as jlongArray but taking simple jlong only. Due to this issue I cannot create array of long like long[]={1L,2L} and pass it to above Java method to call appropriate JNI method.

I want SWIG to generate interface in such a way that I can pass above mentioned array to my C++ method.

I have read this question, but it didn't help me see how to pass an array from Java to C++.

like image 249
Premal Avatar asked Nov 03 '22 22:11

Premal


1 Answers

What you've done here with array_functions is correct and usable, but it's focused on wrapping the C++ code directly, and it won't be using an underlying Java array. You can use it with something like:

SWIGTYPE_p_long_long array = MyLib.new_long_long_array(100); // new array with 100 elements.
for (int i = 0; i < 100; ++i) {
  long_long_array_setitem(array, i, i);
}
new Test().SelectValues(array);

where array is just a proxy to a "real" C++ chunk of memory that you can read/write from on the Java side and pass to wrapped functions.

I'm guessing from your question that you're interested in making this feel more "natural" on the Java side. SWIG also provides array_class which wraps an array similarly, but as a proper object rather than a collection of static functions. For example if you changed your interface file to use array_class(long long, LongLongArray) instead of array_functions you can do:

LongLongArray array = new LongLongArray(100);
for (int i = 0; i < 100; ++i) {
   array.setitem(i,i); 
}
new Test().SelectValues(array.cast());

You can actually make SWIG do more than that with a few typemaps if you want to. Your example class doesn't take a length in SelectValues so I'm assuming you're 0 terminating the array although you can equally well pass the length in with a few simple changes.

(For convenience I %inlined your class to reduce the number of files and added a dummy implementation of it for testing)

%module MyLib

%{
#include <iostream>
%}

%typemap(jtype) long long values[] "long[]"
%typemap(jstype) long long values[] "long[]"
%typemap(javain) long long values[] "$javainput"
%typemap(jni) long long values[] "jlongArray"
%typemap(in) long long values[] {
  jboolean isCopy;
  $1 = JCALL2(GetLongArrayElements, jenv, $input, &isCopy);
}

%inline %{
class Test
{
public:
  void SelectValues(long long values[]) {
    while (*values) {
      std::cout << *values++ << "\n";
    }
  }
};
%}

Here we said that both the proxy class SWIG generates and the JNI class it generates are going to be working with long[], i.e. a Java array. We don't need to do anything in the Java Proxy to Java JNI conversion, so the javain typemap is just a straight pass through. On the C++ side of the JNI that's a jlongArray, which we also specified in another typemap.

We then need an in typemap to arrange converting from jlongArray to long long[] in the C++ side - there's a single JNI call for that and we don't care if it's a copy or the actual memory from the JVM that we end up using. (You might care if you wanted to modify the results and make it visible back inside Java for example)

I tested this with:

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("mylib");
    long arr[] = {100,99,1,0}; // Terminate with 0!
    new Test().SelectValues(arr);
  }
}

Which did exactly as you'd hope.

like image 182
Flexo Avatar answered Nov 12 '22 14:11

Flexo