我在swig中使用类型图以及如何使用数组时有点迷失。我准备了一个使用swig在java和c之间使用数组的工作示例,但我不知道这是否是正确的方法。
基本上我想将一个字节数组byte[]
从java传递给c作为符号char *`+它的大小,在c中修改它并查看java中的更改并在c中创建一个数组并使用它在Java。
我看看了这些问题: How to pass array(array of long in java) from Java to C++ using Swig, Pass an array to a wrapped function as pointer+size or range, How can I make Swig correctly wrap a char* buffer that is modified in C as a Java Something-or-other?
事实上,使用这些解决方案作为制作示例的指南。
这是我在文件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代码中,我为每个函数提供了一个类型映射,我认为我可以重用其中的一些,但是如果还有其他函数函数具有相同的参数,我不想打印它们,我怎么能这样做,我可能无法修改函数的参数名称。
我已经看到了这个问题How do I pass arrays from Java to C++ using Swig?中描述的carrays.i可能性,但是这意味着如果数组的大小是1000个项目并且我想通过Java Socket发送它或者从它创建一个String ,我必须为每个数组项进行1次JNI调用。我实际上想要Java端的 byte[]
,而不是一组访问底层数组的函数,因此现有的代码无需修改即可运行。
上下文: 我想要实现这一点的原因是有一个库有一些功能,但这里的重要部分是它允许从库中导入和导出数据,利用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: convert return type std::string(binary) to java byte[]
所以我的想法是为序列化的Protobuf返回Java byte[]
(与协议缓冲区的Java版本一样),并接受byte[]
来解析protobufs。为避免在导出的第二个参数中获得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[]
。
答案 0 :(得分:5)
不要自动折扣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()
以释放您在分配到新Java阵列后分配的内存。
您可以通过类型和选择性地应用类型映射参数名称。 See this document for more on typemap matching rules。您也可以请求明确使用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
用作数组 - 您可以添加允许此操作的字体图,或者使用{{ 1}}提供不需要空数组的重载。
答案 1 :(得分:0)
这里有详细记录: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<len; ++i)
printf("%x ", data[i]);
printf("\n");
}
%}