使用SWIG将数组从C返回给Java

6
我有一个像这样的C函数:
void get_data(const obj_t *obj, short const **data, int *data_len);

我特意这样写是为了适应 Swig,因为
const short *get_data(const obj_t *obj, int *data_len);

“causes trouble, as SWIG的类型映射不够智能,无法将data_len与返回值关联起来。”
“在Java中,我希望能够像这样调用此函数:”
short data[]= mylib.get_data(obj);

但我不知道如何将数组输出参数变为返回值。对于Ruby和Python,这很容易实现,因为它们的SWIG支持将输出参数作为返回值(由于语言可以有多个返回值)。
那么,我该如何在Java中实现这个功能呢?

你还在寻找解决方案吗? - Flexo
是的,我仍然不知道如何做到这一点。 - paleozogt
我已经用与您所要求的Java语法完全匹配的答案进行了回答。不过,如果您可以在不填充数组的情况下查询大小,则有可能使用原始的C函数进行回答。如果您感兴趣,我会将其添加到我的答案中。 - Flexo
1个回答

9
我已经编写了以下测试头文件来演示问题: ```

我已经编写了以下测试头文件来演示问题:

```
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;
}

我们希望在Java端使用short[]作为返回类型,因此需要让SWIG进行相应的转换:

%typemap(jstype) const short *get_data "short[]"
%typemap(jtype) const short *get_data "short[]"

在JNI端中,我们可以直接传递jshortArray的返回值,无需构建代理类型:

%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中的数组基本上是通过引用传递的,例如:
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]);
  }
}

2
为方便测试,我使用的完整接口文件已上传至我的站点:http://static.lislan.org.uk/~ajw/javaarrout.i - 即使该链接失效,答案也不会受到影响,因为它可以从答案本身中重现。 - Flexo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接