使用SWIG与arrays交互的正确方法

我在swig中使用类型图以及如何使用数组时有点迷失。 我已经准备了一个使用swig在java和c之间使用数组的工作示例,但我不知道它是否是正确的方法。

基本上我想将一个字节数组byte[]从java传递给c作为符号char *`+它的大小,在c中修改它并查看java中的更改并在c中创建一个数组并在Java中使用它。

我看一下theese的问题: 如何使用Swig将数组(java中的long数组)从Java传递到C ++ , 将数组作为指针+大小或范围传递给包装函数 , 如何让Swig正确包装char *在C中修改为Java Something-other的缓冲区?

事实上,使用这些解决方案作为制作示例的指南。

这是我在文件arrays.h中的代码:

 #include  bool createArray(signed char ** arrCA, int * lCA){ *lCA = 10; *arrCA = (signed char*) calloc(*lCA, sizeof(signed char)); for(int i = 0; i < *lCA; i++){ (*arrCA)[i] = i; } return *arrCA != NULL; } bool readArray(const signed char arrRA[], const int lRA){ for(int i = 0; i < lRA; i++){ std::cout << ((unsigned int) arrRA[i]) << " "; } std::cout << std::endl; return true; } bool modifyArrayValues(signed char arrMA[], const int lMA){ for(int i = 0; i < lMA; i++){ arrMA[i] = arrMA[i] * 2; } return true; } bool modifyArrayLength(signed char arrMALIn[], int lMALIn, signed char ** arrMALOut, int * lMALOut){ *lMALOut = 5; *arrMALOut = (signed char*) calloc(*lMALOut, sizeof(signed char)); for(int i = 0; i < *lMALOut; i++){ (*arrMALOut)[i] = arrMALIn[i]; } return true; } 

这是swig(arrays.i)的.i文件:

 %module arrays %{ #include "arrays.h" %} %typemap(jtype) bool createArray "byte[]" %typemap(jstype) bool createArray "byte[]" %typemap(jni) bool createArray "jbyteArray" %typemap(javaout) bool createArray { return $jnicall; } %typemap(in, numinputs=0) signed char ** arrCA (signed char * temp) "$1=&temp;" %typemap(in, numinputs=0) int * lCA (int l) "$1=&l;" %typemap(argout) (signed char ** arrCA, int * lCA) { $result = JCALL1(NewByteArray, jenv, *$2); JCALL4(SetByteArrayRegion, jenv, $result, 0, *$2, (const jbyte*) *$1); } %typemap(out) bool createArray { if (!$1) { return NULL; } } %typemap(jtype) (const signed char arrRA[], const int lRA) "byte[]" %typemap(jstype) (const signed char arrRA[], const int lRA) "byte[]" %typemap(jni) (const signed char arrRA[], const int lRA) "jbyteArray" %typemap(javain) (const signed char arrRA[], const int lRA) "$javainput" %typemap(in,numinputs=1) (const signed char arrRA[], const int lRA) { $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); $2 = JCALL1(GetArrayLength, jenv, $input); } %typemap(freearg) (const signed char arrRA[], const int lRA) { // Or use 0 instead of ABORT to keep changes if it was a copy JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT); } %typemap(jtype) (signed char arrMA[], const int lMA) "byte[]" %typemap(jstype) (signed char arrMA[], const int lMA) "byte[]" %typemap(jni) (signed char arrMA[], const int lMA) "jbyteArray" %typemap(javain) (signed char arrMA[], const int lMA) "$javainput" %typemap(in, numinputs=1) (signed char arrMA[], const int lMA) { $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); $2 = JCALL1(GetArrayLength, jenv, $input); } %typemap(freearg) (signed char arrMA[], const int lMA) { JCALL3(ReleaseByteArrayElements, jenv, $input, $1, 0); } %typemap(jtype) (signed char arrMALIn[], int lMALIn) "byte[]" %typemap(jstype) (signed char arrMALIn[], int lMALIn) "byte[]" %typemap(jni) (signed char arrMALIn[], int lMALIn) "jbyteArray" %typemap(javain) (signed char arrMALIn[], int lMALIn) "$javainput" %typemap(in, numinputs=1) (signed char arrMALIn[], int lMALIn) { $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); $2 = JCALL1(GetArrayLength, jenv, $input); } %typemap(freearg) (signed char arrMALIn[], int lMALIn) { JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT); } %typemap(jtype) bool modifyArrayLength "byte[]" %typemap(jstype) bool modifyArrayLength "byte[]" %typemap(jni) bool modifyArrayLength "jbyteArray" %typemap(javaout) bool modifyArrayLength { return $jnicall; } %typemap(in, numinputs=0) signed char ** arrMALOut (signed char * temp) "$1=&temp;" %typemap(in, numinputs=0) int * lMALOut (int l) "$1=&l;" %typemap(argout) (signed char ** arrMALOut, int * lMALOut) { $result = JCALL1(NewByteArray, jenv, *$2); JCALL4(SetByteArrayRegion, jenv, $result, 0, *$2, (const jbyte*) *$1); } %typemap(out) bool modifyArrayLength { if (!$1) { return NULL; } } %include "arrays.h" 

最后是Java代码来测试它:

 public class Run{ static { System.loadLibrary("Arrays"); } public static void main(String[] args){ byte[] test = arrays.createArray(); printArray(test); arrays.readArray(test); arrays.modifyArrayValues(test); printArray(test); byte[] test2 = arrays.modifyArrayLength(test); printArray(test2); } private static void printArray(byte[] arr){ System.out.println("Array ref: " + arr); if(arr != null){ System.out.println("Array length: " + arr.length); System.out.print("Arrays items: "); for(int i =0; i < arr.length; i++){ System.out.print(arr[i] + " "); } } System.out.println(); } } 

该示例有效,但我不确定这是正确的方法,我的意思是:

是否有更简单的方法来实现相同的结果?

这段代码是否有内存泄漏(一方面我认为是因为我做了一个calloc但我没有释放它,但另一方面我将它传递给SetByteArrayRegion,所以也许释放它会导致错误)?

SetByteArrayRegion是复制值还是仅复制引用?,例如,如果不是实际执行calloc而不是通过引用从c ++对象获取数组,当它退出作用域时会被破坏?

返回到Java的数组在取消它时是否正确释放?

有没有办法指定类型映射应用于何处?我的意思是,在.i代码中,我为每个函数提供了一个类型映射,我认为我可以重用其中的一些,但是如果有其他函数具有相同的函数我不想打印它们的参数,我怎么能这样做,我可能无法修改函数的参数名称。

我已经看到了这个问题中描述的carrays.i可能性如何使用Swig 将数组从Java传递到C ++? ,但这意味着如果数组的大小是1000个项目,并且我想通过Java Socket发送它或从中创建一个String,我必须为每个数组项进行1次JNI调用。 我实际上想在Java端使用byte[] ,而不是一组访问底层数组的函数,因此现有的代码无需修改即可运行。


上下文:我想要实现这一点的原因是有一个库具有一些function,但这里的重要部分是它允许使用Google Protocols Buffers从库中导入和导出数据。 所以与这个问题相关的代码如下所示:

 class SomeLibrary { bool export(const std::string & sName, std::string & toExport); bool import(const std::string & sName, const std::string & toImport); } 

问题是C ++中的Protobuf使用std :: string来存储数据,但是这个数据是二进制的,所以它不能作为普通的Java String返回,因为它被截断了,更多的是在Swig中:转换返回类型std ::字符串(二进制)到java byte [] 。

所以我的想法是为序列化的Protobuf返回Java一个byte[] (与协议缓冲区的Java版本一样),并接受byte[]来解析protobufs。 为了避免在SWIGTYPE_p_std_string的第二个参数中获取SWIGTYPE_p_std_string ,并且使用导入的第二个参数的字符串y已使用%extend包装这两个函数,如下所示:

 %extend SomeLibrary{ bool export(const std::string & sName, char ** toExportData, int * toExportLength); bool import(const std::string & sName, char * toImportData, int toImportLength); } 

现在我应该能够制作打字机了。

但为了更加通用,我要求操作从Java到SWIG的数组, 使用本机Java byte[]

不要自动折扣carrays.i。 那说SWIG已经有一些方便的类型图:

 %module test %apply(char *STRING, size_t LENGTH) { (char *str, size_t len) }; %inline %{ void some_func(char *str, size_t len) { } %} 

这在Java接口中产生一个函数:

 public static void some_func(byte[] str) 

也就是说它需要一个像普通人一样用Java构建的数组,并为你填充指针和长度。 几乎是免费的。

您的代码几乎肯定会泄漏 – 您希望在argout类型映射中调用free()以释放您在复制到新Javaarrays后分配的内存。

您可以根据参数的类型名称选择性地应用类型映射。 有关typemap匹配规则的更多信息,请参阅此文档 。 您还可以请求显式使用typemap,否则它将不会与%apply一起使用,如上面显示的示例所示。 (实际上它复制了这些类型图,所以如果你只修改其中一个,那么在一般情况下它不会替换它)

通常,将数组从Java传递到C ++或使用已知大小的数组的类型映射比从C ++返回到Java的数组更简单,因为大小信息更明显。

我的建议是计划在Java内部进行大量的分配,并设计可能使数组在两种模式下运行的函数:一个表示所需的大小,另一个表示实际工作。 您可以这样做:

 ssize_t some_function(char *in, size_t in_sz) { if (in_sz < the_size_I_need) { return the_size_I_need; // query the size is pretty fast } // do some work on in if it's big enough // use negative sizes or exceptions to indicate errors return the_size_I_really_used; // send the real size back to Java } 

这将允许您在Java中执行以下操作:

 int sz = module.some_function(new byte[0]); byte result[] = new byte[sz]; sz = module.some_function(result); 

请注意,对于默认的类型映射,需要new byte[0] ,因为它们不允许将null用作数组 - 如果需要,可以添加允许此类型的类型映射,或者使用%extend来提供无法执行的重载需要一个空数组。

这里有很好的文档: http : //swig.org/Doc3.0/SWIGDocumentation.html#Java_binary_char

下面的接口文件将在java中生成byte []方法:

 %apply (char *STRING, size_t LENGTH) { (const char data[], size_t len) } %inline %{ void binaryChar1(const char data[], size_t len) { printf("len: %d data: ", len); for (size_t i=0; i