Java 使用SWIG与阵列交互的正确方法

Java 使用SWIG与阵列交互的正确方法,java,c,swig,Java,C,Swig,我对swig中的类型映射以及如何使用数组有点迷茫。我已经准备了一个使用swig在java和c之间使用数组的工作示例,但我不知道这是否是正确的方法 基本上,我想把一个字节数组byte[]作为一个“有符号字符*”+从java传递到c,在c中修改它,查看java中的变化,在c中创建一个数组,并在java中使用它 我已经看了这些问题: , , 事实上,我们是以这些解决方案为指导来做例子的 这是我在arrays.h文件中的代码: #include <iostream> bool cre

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

基本上,我想把一个字节数组
byte[]
作为一个“有符号字符*”+从java传递到c,在c中修改它,查看java中的变化,在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;
}
最后是测试它的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();
    }
}

是C++中的原BUTF使用STD::String来存储数据,但是这个数据是二进制的,所以它不能被返回作为一个普通的java字符串,因为它被截断,更多的是在.< /P>中。 因此,我的想法是为序列化的Protobuf返回Java a

byte[]
(就像Java版本的协议缓冲区一样),并接受
byte[]
来解析Protobuf。为了避免在导出的第二个参数中获取
SWIGTYPE_p_std_string
,并在导入的第二个参数中使用string,y使用%extend包装了这两个函数,如下所示:

class SomeLibrary {

  bool export(const std::string & sName, std::string & toExport);

  bool import(const std::string & sName, const std::string & toImport);

}
%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
字节[]

不要打折carrays。我会自动执行。也就是说,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)
i、 它需要一个数组,你可以像普通的一样用Java构建,并为你填充指针和长度。几乎是免费的

目前的代码几乎肯定会泄漏—您希望在argout typemap中调用
free()
,以释放分配的内存,一旦它被复制到新的Java数组中

您可以根据参数的类型和名称有选择地应用类型映射。您还可以请求显式地使用类型映射,否则它将不会与
%apply
一起使用,如我上面展示的示例所示。(实际上,它复制了类型映射,因此,如果您只修改了其中一个,则在一般情况下不会替换它)

一般来说,将数组从java传递到C++或使用已知大小数组的类型映射比C++从java返回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
}

请注意,对于默认类型映射,需要
新字节[0]
,因为它们不允许将
null
用作数组-如果需要,可以添加允许此操作的类型映射,或者使用
%extend
提供不需要空数组的重载。

这里有详细说明:

下面的接口文件将生成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");
}
%}
%apply(字符*字符串,大小\长度){(常量字符数据[],大小\长度)}
%内联%{
void binaryChar1(常量字符数据[],大小长度){
printf(“len:%d数据:”,len);

对于(sisixt i=0;II认为你正在使它比它需要的复杂):我想你想在java中建立一个大数组然后把它传递到C++函数中吗?我可以显示一个简单的解决方案,并且在路上也希望解决一些其他问题。例如,你想通过<代码>字节[]
转换为类似于
void foo(const char*arr,size\t len)的函数
?@Flexo-我也认为我做的太复杂了。我也编辑了这个问题,提供了更多的上下文。要回答更多的几点:
SetByteArrayRegion
复制数据。Java数组将由通常的Java机制管理。它是在JNI调用本地创建的,并将在对它的ast引用被删除。我已经测试了这个解决方案,对输入值非常有效。对于输出,我无法事先知道所需的大小,我的意思是它正在序列化一个protobuf,所以我只知道序列化时需要多少空间,我不想丢弃序列化的数据,因为它不适合我传递的数组,我是一个lso不希望返回到Java的数组比包含的数据大,因为现有代码在没有更改的情况下无法工作(这可以通过在Java端复制数组来避免).我已经更改了输入到解决方案的类型图,以及原始的输出typemaps@JavierMr这也是有道理的——我会使用类似于这个答案的输入(或组合输入/输出,因为类型映射会将值复制回来)以及创建新数组。非常感谢您对我所做的关于swig的所有问题的帮助。@flex,当我根据您的示例构建%module测试时,生成的java输出代码是
public static void some_func(String str)
。输入参数是String,而不是byte[].No,
%inline
不会产生不同的效果,它相当于写两次,一次在
%{%}
内部,一次不在内部。如果不起作用,可能您有不同的顺序或不同的变量名,因此
%apply
无法正确应用gtet
int sz = module.some_function(new byte[0]);
byte result[] = new byte[sz];
sz = module.some_function(result);
%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");
}
%}