我有这样的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);
导致麻烦,因为SWIG的类型映射不够智能,无法将data_len与返回值相关联.
short data[]= mylib.get_data(obj);
但我无法弄清楚如何让数组输出参数成为返回值.使用Ruby和Python,这可以正常工作,因为这些语言的SWIG支持返回输出参数作为返回值(因为语言可以有多个返回值).
我怎样才能使用Java?
解决方法
我把以下测试头文件放在一起来演示问题:
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参数设置一个typemap.它不需要在Java端可见,因为数组已知长度,但我们需要为指针指定一些存储空间,我们确保它持续足够长的时间以便我们以后可以读取它将数组返回给Java时.
%typemap(in,numinputs=0,noblock=1) int *data_len { int temp_len; $1 = &temp_len; }
然后我们希望SWIG在Java端使用short []作为返回类型:
%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数组中.如果需要,我们应该在这里释放()真实结果数组,但在我的例子中,它是静态分配的,因此不需要被释放.
%typemap(out) const short *get_data { $result = JCALL1(NewShortArray,jenv,temp_len); JCALL4(SetShortArrayRegion,$result,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,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]); } }