我对 swig 中的类型映射以及如何使用数组有点迷茫。我准备了一个使用 swig 在 java 和 c 之间使用数组的工作示例,但我不知道这是否是正确的方法。
基本上我想将一个字节数组byte[]
从java传递给c作为'signed char *` +它的大小,在c中修改它并查看java中的变化并在c中创建一个数组并在Java中使用它。
我看看这些问题: 如何使用 Swig 将数组(java 中的 long 数组)从 Java 传递到 C++, 将数组作为指针+大小或范围传递给包装函数, 如何使 Swig 正确包装一个字符* 在 C 中修改为 Java 的缓冲区?
事实上,使用这些解决方案作为指导来制作示例。
这是我在文件 arrays.h 中的代码:
#include <iostream>
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 可能性How do I pass arrays from Java to C++ using Swig? ,但这意味着如果数组的大小是 1000 项,并且我想通过 Java 套接字发送它或从中创建一个字符串,我必须为每个数组项进行 1 个 JNI 调用。而且我实际上想要一个byte[]
在 Java 端,而不是一组函数来访问底层数组,所以已经存在的代码无需修改即可工作。
上下文:我想要实现这一点的原因是有一个具有某些功能的库,但这里的重要部分是它允许使用 Google 协议缓冲区从库中导入和导出数据。所以与这个问题相关的代码如下所示:
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 字符串返回,因为它会被截断,更多的是在Swig 中:转换返回类型 std:: string(binary) 到 java byte[]。
所以我的想法是为序列化的 Protobuf 返回 Java a byte[]
(就像协议缓冲区的 Java 版本一样)并接受byte[]
解析 protobuf。为了避免进入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[]
。