当前位置: 首页 > 知识库问答 >
问题:

使用SWIG与数组交互的正确方式

罗奇文
2023-03-14

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

基本上,我想把一个字节数组作为“有符号字符*”的大小从java传递到c,在c中修改它,查看java中的变化,在c中创建一个数组,并在java中使用它。

我已经研究了这些问题:如何使用Swig将数组(java中的long数组)从java传递到C,将数组作为指针大小或范围传递到已包装的函数,如何使Swig正确包装在C中作为java某物修改的char*缓冲区?

事实上,用解决方案作为指导来做例子。

这是我在文件数组中的代码。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是否在无效时正确释放?

有没有办法指定typemap从何处应用到何处?,我的意思是,在。我编码我为每个函数提供了一个类型映射,我想我可以重用其中的一些,但是如果有其他函数具有相同的参数,我不想将其类型映射,我如何才能做到这一点,我可能无法修改函数的参数名称。

我看到了carrays。我在这个问题中描述了如何使用Swig将数组从Java传递到C?,但这意味着,如果数组的大小是1000个项目,并且我想通过Java套接字发送它或从中创建一个字符串,那么我必须为每个数组项目调用1个JNI。实际上,我希望Java端有一个字节,而不是一组访问底层数组的函数,因此现有代码可以不经修改地工作。

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

class SomeLibrary {

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

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

}

事情是,在C中的原型buf使用std::字符串来存储数据,但这个数据是二进制的,所以它不能作为一个正常的Java字符串返回,因为它被截断,更多的是在Swig:转换返回类型std::字符串(二进制)到java字节[]。

所以我的想法是返回到Java一个byte[]作为序列化的协议缓冲区(Java版本的协议缓冲区也是如此),并接受byte[]来解析协议。为了避免在导出的第二个参数中获取SWIGTYPE_p_std_string,并且使用String作为导入的第二个参数y已经使用%扩展包装了这两个函数,如下所示:

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

共有2个答案

颛孙英勋
2023-03-14

这里有详细的记录: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");
}
%}
卫焕
2023-03-14

不要自动打折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)

i、 它采用一个可以像普通一样在Java中构建的数组,并为您填充指针和长度。几乎是免费的。

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

您可以根据参数的类型和名称有选择地应用类型映射。有关typemap匹配规则的详细信息,请参阅本文档。您还可以请求显式使用typemap,否则它不会与应用一起使用,如我上面显示的示例所示。(实际上,它复制了类型映射,因此如果您只修改了其中一个类型映射,一般情况下不会替换它)

通常,用于将数组从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);

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

 类似资料:
  • 问题内容: 我对swig中的typemap和如何使用数组感到迷茫。我准备了一个使用swig在java和c之间使用数组的工作示例,但是我不知道这是否是正确的方法。 基本上,我想将字节数组从Java 传递给c作为“ signed char *” +它的大小,在c中对其进行修改并查看java中的更改,并在c中创建一个数组并在Java中使用它。 *在C中被修改为Java或其他的缓冲区?](http://c

  • 我是新使用Spring Webflux的人,我能够用代码将两个rest API之间的结果连接起来: 项目是单声道,孩子是单声道 这是一个好的实现,还是一个浪费反应式编程功能的阻塞性实现

  • 本文向大家介绍你有使用做过vue与原生app交互吗?说说vue与ap交互的方法相关面试题,主要包含被问及你有使用做过vue与原生app交互吗?说说vue与ap交互的方法时的应答技巧和注意事项,需要的朋友参考一下 用WebViewJavascriptBridge

  • 问题内容: 我有以下代码供用户单击按钮使用,当他们单击按钮时,正在查看的特定字符串将被收藏并存储在其他位置。 我有两个问题。 我现在所拥有的是什么问题?因为当您单击按钮时它会崩溃。 您将如何完成将要加载的字符串并将其保存到数组的load array方法,以便用户以后可以看到该数组? 谢谢你的时间!! 原木猫 问题答案: 函数返回。您可能想要像这样使用它: 该数组可以稍后在第二部分中使用。希望这可以

  • 我想使用swig从C附加到Javabyte[]。诀窍是在不调用GetByteArrayElements的情况下执行此操作,因为如果无法固定byte[],这将生成byte[]的副本。 没有swig,我们可以使用SetByteArrayArea轻松做到这一点-直接将字节数写入byte[]。swg的问题是: > 执行实际工作的代码在. cpp中-例如读取文件的内容并将其放入字符串fileContents

  • JavaScript 调用 Krpano 对象 embedpano({ //省略其它不相关设置... id: "krpanoSWFObject", onready: krpanoReady }); function krpanoReady() { var krpano = document.getElementById('krpanoSWFObject');