Я собрал следующий файл заголовка теста, чтобы продемонстрировать проблему:
typedef struct { } obj_t;
const short *get_data(const obj_t *obj, int *data_len) {
(void)obj;
static short arr[] = {1,2,3,4,5};
*data_len = sizeof(arr)/sizeof(*arr);
return arr;
}
Я поговорю с файлом модуля, который я написал, он начинается довольно стандартно:
%module test
%{
#include "test.h"
%}
Затем мы устанавливаем карту типов для аргумента data_len
. Он не должен быть видимым на стороне Java, так как длина будет известна массиву, но нам нужно организовать некоторое хранилище, чтобы указатель указывал на него, и мы гарантируем, что он длится достаточно долго, чтобы мы могли прочитать его позже при возврате массива в Java тоже.
%typemap(in,numinputs=0,noblock=1) int *data_len {
int temp_len;
$1 = &temp_len;
}
Затем мы хотим, чтобы SWIG использовал short[]
на стороне Java для типа возвращаемого значения:
%typemap(jstype) const short *get_data "short[]"
%typemap(jtype) const short *get_data "short[]"
и jshortArray
на стороне JNI - нет необходимости создавать тип прокси, поэтому мы просто передаем возвращаемое значение напрямую:
%typemap(jni) const short *get_data "jshortArray"
%typemap(javaout) const short *get_data {
return $jnicall;
}
Наконец, мы создаем карту типов, которая собирается создать новый массив, с размером, основанным на длине, возвращаемой функцией, и копируем полученный результат в массив Java для нас. При необходимости мы должны free()
реальный массив результатов здесь, но в моем примере он был статически размещен, поэтому его не нужно освобождать.
%typemap(out) const short *get_data {
$result = JCALL1(NewShortArray, jenv, temp_len);
JCALL4(SetShortArrayRegion, jenv, $result, 0, temp_len, $1);
// If the result was malloc()'d free it here
}
Наконец, мы включаем заголовочный файл для SWIG для переноса, используя только что написанные карты типов:
%include "test.h"
Я проверил это с помощью:
public class run {
public static void main(String argv[]) {
System.loadLibrary("test");
obj_t obj = new obj_t();
short[] result = test.get_data(obj);
for (int i = 0; i < result.length; ++i) {
System.out.println(result[i]);
}
}
}
Который произвел:
1
2
3
4
5
Для справки вы могли бы обернуть:
void get_data(const obj_t *obj, short const **data, int *data_len);
также, хотя, если бы у вашей функции был способ запрашивать размер без установки массива, вы могли бы обернуть это немного умнее, выделив массив правильного размера на стороне Java. Для этого вам нужно написать промежуточную функцию в Java, которая запрашивает размер, настроить вызов и затем вернуть полученный массив. Это позволит вам использовать GetShortArrayElements
/ ReleaseShortArrayElements
для потенциально 0 вызовов копирования.
Это будет работать, потому что массивы в Java в основном передаются по ссылке, например ::1010 *
public class ret {
public static void foo(int arr[]) {
arr[0] = -100;
}
public static void main(String argv[]) {
int arr[] = new int[10];
System.out.println(arr[0]);
foo(arr);
System.out.println(arr[0]);
}
}