我在 C 中有以下结构:
typedef struct KFMutableBytes {
uint8_t * _Nullable bytes;
size_t length;
const size_t capacity;
} KFMutableBytes;
该结构作为指针传递给函数,如下所示:
KFMutableBytes bytes = ...;
someFunc(&bytes);
该函数写入bytes.bytes
直至bytes.capacity
,并将写入的长度存储在bytes.length
中。
到目前为止我已经得到了这个:
%typemap(jni) KFMutableBytes * "jbyteArray"
%typemap(jtype) KFMutableBytes * "byte[]"
%typemap(jstype) KFMutableBytes * "byte[]"
%typemap(in) KFMutableBytes * {
KFMutableBytes *bytes = (KFBytes *)malloc(sizeof(KFMutableBytes));
if(bytes == NULL) {
jclass clazz = (*jenv)->FindClass(jenv, "java/lang/OutOfMemoryError");
(*jenv)->ThrowNew(jenv, clazz, "Not enough memory");
return $null;
}
KFMutableBytes b = KFMutableBytesCreate((uint8_t *) JCALL2(GetByteArrayElements, jenv, $input, 0), 0, (size_t) JCALL1(GetArrayLength, jenv, $input));
memcpy(bytes, &b, sizeof(b));
$1 = bytes;
}
%typemap(javain) KFMutableBytes * "$javainput"
/* Prevent default freearg typemap from being used */
%typemap(freearg) KFMutableBytes * {
JCALL3(ReleaseByteArrayElements, jenv, $input, (jbyte *) $1->bytes, 0);
free($1);
}
这意味着在 Java 中,someFunc
是 someFunc(byte[] bytes)
。问题是你无法获取写入的长度,并且无法在 C 中修改字节数组长度。所以实际上我只需要将 byte[]
映射到 bytes
和 capacity
成员,并将 length 成员映射到 long
。但我不确定如何将字节数组映射到结构成员?
最佳答案
我已经解决了这个问题,但是解决方案很复杂。本质上,我忽略了结构体的属性,将我自己的构造函数和属性添加到 Java 中的类中,将结构体的 Java 表示直接传递给 C,然后我提取 Java 属性来创建 C 结构体,然后在函数执行之后调用,将长度写回java对象并释放数组。
// ignore the properties of the struct to prevent them being added to the Java class
%ignore KFMutableBytes::bytes;
%ignore KFMutableBytes::capacity;
%ignore KFMutableBytes::length;
// Add our own constructors, properties and methods to the Java class
%extend KFMutableBytes {
#if defined(SWIG)
%proxycode %{
private byte[] bytes;
private int length;
public byte[] getBytes() {
if(length == 0) { return new byte[0]; }
return Arrays.copyOfRange(bytes, 0, length);
}
public KFMutableBytes(byte[] bytes) {
this.bytes = bytes;
length = 0;
}
public KFMutableBytes(int capacity) {
this.bytes = new byte[capacity];
length = 0;
}
%}
#endif
}
// Tell SWIG to pass the java object straight to C
%typemap(jni) KFMutableBytes * "jobject"
%typemap(jtype) KFMutableBytes * "KFMutableBytes"
%typemap(jstype) KFMutableBytes * "KFMutableBytes"
// In C, get the added properties from the Java object and set those to our struct
%typemap(in) KFMutableBytes * {
jclass clazz = JCALL1(FindClass, jenv, "com/example/myapplication/cppinterface/KFMutableBytes");
jfieldID fid = JCALL3(GetFieldID, jenv, clazz, "bytes", "[B");
jobject byteArrayObj = JCALL2(GetObjectField, jenv, $input, fid);
jbyteArray byteArray = *(jbyteArray *)&byteArrayObj;
jfieldID fid2 = JCALL3(GetFieldID, jenv, clazz, "length", "I");
int length = JCALL2(GetIntField, jenv, $input, fid2);
KFMutableBytes *bytes = (KFBytes *)malloc(sizeof(KFMutableBytes));
if(bytes == NULL) {
jclass clazz = (*jenv)->FindClass(jenv, "java/lang/OutOfMemoryError");
(*jenv)->ThrowNew(jenv, clazz, "Not enough memory");
return $null;
}
// Because ->capacity is const, we can't set it directly, we have to create another struct and copy to our allocated struct
KFMutableBytes b = KFMutableBytesCreate((uint8_t *) JCALL2(GetByteArrayElements, jenv, byteArray, 0), length, (size_t) JCALL1(GetArrayLength, jenv, byteArray));
memcpy(bytes, &b, sizeof(b));
$1 = bytes;
}
%typemap(javain) KFMutableBytes * "$javainput"
// When finished with the struct, set the length back to the object and release the byte array before freeing
%typemap(freearg) KFMutableBytes * {
jclass clazz = JCALL1(FindClass, jenv, "com/example/myapplication/cppinterface/KFMutableBytes");
jfieldID fid = JCALL3(GetFieldID, jenv, clazz, "bytes", "[B");
jobject byteArrayObj = JCALL2(GetObjectField, jenv, $input, fid);
jbyteArray byteArray = *(jbyteArray *)&byteArrayObj;
jfieldID fid2 = JCALL3(GetFieldID, jenv, clazz, "length", "I");
JCALL3(SetIntField, jenv, $input, fid2, (int)$1->length);
JCALL3(ReleaseByteArrayElements, jenv, byteArray, (jbyte *) $1->bytes, 0);
free($1);
}
关于java - 使用 SWIG 将字节数组转换为 C 结构体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62778840/