正确的方法使用痛饮阵列互动 [英] Correct way to interact with arrays using SWIG

查看:134
本文介绍了正确的方法使用痛饮阵列互动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有点在痛饮,以及如何使用数组typemaps丢失。我有prepared使用痛饮使用Java和C之间的阵列工作的例子,但我不知道它是做了正确的道路。

基本上我想传递一个字节数组字节[] 从Java到C作为'signed的char *`+它的大小,修改它在C和看到的变化Java和在C创建一个数组并在Java中使用它。

我看看theese问题:
<一href=\"http://stackoverflow.com/questions/10947560/how-to-pass-arrayarray-of-long-in-java-from-java-to-c-using-swig\">How从Java传递数组(在Java中的多头排列),以C ++使用痛饮,
<一href=\"http://stackoverflow.com/questions/11584599/pass-an-array-to-a-wrapped-function-as-pointersize-or-range/11584600#11584600\">Pass一个数组来包裹函数指针+规模或范围,
<一href=\"http://stackoverflow.com/questions/2740068/how-can-i-make-swig-correctly-wrap-a-char-buffer-that-is-modified-in-c-as-a-jav\">How我可以做正确痛饮换一个char *缓冲区在C修改为一个Java的东西或 - 其他?

和实际上使用的解决方案为指导,以使示例。

这是我的code在文件arrays.h:

 的#include&LT;&iostream的GT;布尔createArray(符号的字符** arrCA,为int * LCA){
    * LCA = 10;
    * arrCA =(符号字符*)释放calloc(* LCA,sizeof的(有符号字符));    的for(int i = 0; I&LT; * LCA;我++){
        (* arrCA)[我] =我;
    }    返回* arrCA!= NULL;
}布尔readArray(常量符号字符arrRA [],const int的LRA){
    的for(int i = 0; I&LT; LRA;我++){
        性病::法院LT&;&LT; ((无符号整数)arrRA [I])LT;&LT; ;
    }
    性病::法院LT&;&LT;的std :: ENDL;
    返回true;
}布尔modifyArrayValues​​(符号字符arrMA [],const int的LMA){
    的for(int i = 0; I&LT; LMA;我++){
        arrMA [I] = arrMA [I] * 2;
    }
    返回true;
}
布尔modifyArrayLength(符号字符arrMALIn [],INT lMALIn,符号的字符** arrMALOut,为int * lMALOut){    * lMALOut = 5;
    * arrMALOut =(符号字符*)释放calloc(* lMALOut,sizeof的(有符号字符));    的for(int i = 0; I&LT; * lMALOut;我++){
        (* arrMALOut)[我] = arrMALIn [I]
    }
    返回true;
}

这是在痛饮.i文件(arrays.i):

 %模块阵列%{
    #包括arrays.h
%}%类型映射(jtype的)布尔createArray字节[]
%类型映射(jstype)布尔createArray字节[]
%类型映射(JNI)布尔createArrayjbyteArray
%类型映射(javaout)布尔createArray {$回报JNICALL; }
%类型映射(中,numinputs = 0)签署的char ** arrCA(符号字符*临时)$ 1 =&安培;温度;
%类型映射(中,numinputs = 0)为int * LCA(INT L)$ 1 =&安培;升;
%类型映射(argout)(符号的字符** arrCA,为int * LCA){
    $结果= JCALL1(NewByteArray,jenv,* $ 2);
    JCALL4(SetByteArrayRegion,jenv,$结果,0,* $ 2,(常量jbyte *)* $ 1);
}
%类型映射(出)布尔createArray {
    如果(!$ 1){
        返回NULL;
    }
}
%类型映射(jtype的)(常量符号字符arrRA [],const int的LRA)的byte []
%类型映射(jstype)(常量符号字符arrRA [],const int的LRA)的byte []
%类型映射(JNI)(常量符号字符arrRA [],const int的LRA)jbyteArray
%类型映射(javain)(常量符号字符arrRA [],const int的LRA)$ javainput%类型映射(中,numinputs = 1)(常量符号字符arrRA [],const int的LRA){
  $ 1 = JCALL2(GetByteArrayElements,jenv,$输入,NULL);
  $ 2 = JCALL1(GetArrayLength,jenv,$输入);
}%类型映射(freearg)(常量符号字符arrRA [],const int的LRA){
  //或者使用0,而不是中止保持变化,如果它是一个副本
  JCALL3(ReleaseByteArrayElements,jenv,$投入,$ 1,JNI_ABORT);
}
%类型映射(jtype的)(符号字符arrMA [],const int的LMA)的byte []
%类型映射(jstype)(符号字符arrMA [],const int的LMA)的byte []
%类型映射(JNI)(符号字符arrMA [],const int的LMA)jbyteArray
%类型映射(javain)(符号字符arrMA [],const int的LMA)$ javainput%类型映射(中,numinputs = 1)(符号字符arrMA [],const int的LMA){
    $ 1 = JCALL2(GetByteArrayElements,jenv,$输入,NULL);
    $ 2 = JCALL1(GetArrayLength,jenv,$输入);
}%类型映射(freearg)(符号字符arrMA [],const int的LMA){
  JCALL3(ReleaseByteArrayElements,jenv,$输入,$ 1,0);
}%类型映射(jtype的)(符号字符arrMALIn [],诠释lMALIn)的byte []
%类型映射(jstype)(符号字符arrMALIn [],诠释lMALIn)的byte []
%类型映射(JNI)(符号字符arrMALIn [],诠释lMALIn)jbyteArray
%类型映射(javain)(符号字符arrMALIn [],诠释lMALIn)$ javainput%类型映射(中,numinputs = 1)(符号字符arrMALIn [],诠释lMALIn){
    $ 1 = JCALL2(GetByteArrayElements,jenv,$输入,NULL);
    $ 2 = JCALL1(GetArrayLength,jenv,$输入);
}%类型映射(freearg)(符号字符arrMALIn [],诠释lMALIn){
    JCALL3(ReleaseByteArrayElements,jenv,$投入,$ 1,JNI_ABORT);
}%类型映射(jtype的)布尔modifyArrayLength字节[]
%类型映射(jstype)布尔modifyArrayLength字节[]
%类型映射(JNI)布尔modifyArrayLengthjbyteArray
%类型映射(javaout)布尔modifyArrayLength {$回报JNICALL; }
%类型映射(中,numinputs = 0)签署的char ** arrMALOut(符号字符*临时)$ 1 =&安培;温度;
%类型映射(中,numinputs = 0)为int * lMALOut(INT L)$ 1 =&安培;升;
%类型映射(argout)(符号的字符** arrMALOut,为int * lMALOut){
    $结果= JCALL1(NewByteArray,jenv,* $ 2);
    JCALL4(SetByteArrayRegion,jenv,$结果,0,* $ 2,(常量jbyte *)* $ 1);
}
%类型映射(出)布尔modifyArrayLength {
    如果(!$ 1){
        返回NULL;
    }
}
%包括arrays.h

和最后的Java code来测试它:

 公共类运行{    静态的 {
        的System.loadLibrary(阵列);
    }    公共静态无效的主要(字串[] args){        字节[] =测试arrays.createArray();        printArray(试验);        arrays.readArray(试验);        arrays.modifyArrayValues​​(试验);        printArray(试验);        字节[] = TEST2 arrays.modifyArrayLength(试验);        printArray(测试2);    }    私有静态无效printArray(字节[] ARR){        的System.out.println(阵列参考:+ ARR);        如果(ARR!= NULL){
            的System.out.println(数组长度:+ arr.length);            System.out.print(阵列项目:);            的for(int i = 0; I&LT; arr.length;我++){
                System.out.print(ARR [I] +);
            }
        }
        的System.out.println();
    }
}

这个例子的作品,但我不知道这是正确的道路,我的意思是:

有更简单的方法来达到同样的效果?

这是否code有内存泄漏(一方面我觉得这是因为我做了释放calloc但我不释放它,但另一方面,我把它传递给SetByteArrayRegion,所以也许释放它会导致错误)?

请问SetByteArrayRegion复制的值或仅参考?例如,如果而不是实际做了释放calloc如果通过将是毁灭,当它退出范围内引用获得从C ++对象数组?

是数组返回到Java作废时,它释放正确?

在那里指定从哪里到哪里一类型映射适用?我的意思是,在.I codeI提供了每个功能,在这里我想我会重用他们有的一个类型映射的方式,但如果有是别人用的功能,我不希望他们的TypeMap相同的参数,我怎么能做到这一点,我可能无法修改的函数的参数名称。

我已经看到了这个问题的我如何传递数组从Java到C ++使用痛饮?,但是这意味着,如果数组的大小为1000个项目,我想通过Java的Socket来发送或从字符串创建它,我必须为每个阵列项目1 JNI调用。实际上我希望有一个 字节[] 在Java端,而不是一组函数访问underlaying的数组,所以现有的code工作无需修改。


上下文:
我想做到这一点的原因是,有有一些功能库,但这里的重要组成部分,是它允许从利用的谷歌协议缓冲器的库导入和导出数据。因此,与此相关的问题code是这样的:

 类SomeLibrary {  布尔出口(常量标准::字符串&放大器; SNAME,标准::字符串&安培; toExport);  布尔进口(常量标准::字符串&放大器; SNAME,常量标准::字符串&安培; toImport);}

的事情是,在的Protobuf C ++使用的std ::字符串来存储数据,但是这个数据是二进制是因为它被截断它不能被恢复为正常的Java字符串,更多的这<一个href=\"http://stackoverflow.com/questions/12192624/swig-convert-return-type-stdstringbinary-to-java-byte/12196522#comment16788430_12196522\">Swig:字符串(二进制)转换返回类型的std ::为Java字节[] 。

所以我的想法是返回到Java中的字节[] 的序列化的Protobuf(如确实协议缓冲区的Java版本),并接受字节[] 解析protobufs。为了避免让 SWIGTYPE_p_std_string 在出口的第二个参数,并具有进口y的第二个参数字符串用%的包裹两种功能扩展,如:

 %延长SomeLibrary {  布尔出口(常量标准::字符串&放大器; SNAME,焦炭** toExportData,为int * toExportLength);  布尔进口(常量标准::字符串&放大器; SNAME,字符* toImportData,INT toImportLength);}

现在我应该能够使typemaps。

但为了更一般的,我问从Java操作数组的一般痛饮,本机的Java 字节[]


解决方案

不要自动打折carrays.i。这就是说痛饮有一些方便typemaps早已:

 %模块的测试%适用(字符*字符串,长度为size_t){(的char * str中,为size_t LEN)};%一致 %{
无效some_func(的char * str中,为size_t的len){
}
%}

其中在Java接口产生功能

 公共静态无效some_func(字节[] STR)

即。它需要,你可以建立在Java中的数组一样正常,在指针和长度为你罢了。几乎是免费的。

,因为它代表几乎可​​以肯定你的code泄漏 - 你要调用免费()的argout类型映射来释放内存中,你一旦它被分配复制到新的Java数组。

您可以选择由双方类型的的该参数的名义申请typemaps。 更多关于类型映射的匹配规则的看到这个文件。您也可以要求明确地使用的地方,否则它不会与使用的类型映射%适用在我上面显示的例子。 (实际上它复制typemaps,所以,如果你只是其中的一个修改它不会在一般情况下更换)

在一般用于从Java传递数组C ++或已知大小的数组工作typemaps比那些简单的从C ++返回到Java,因为大小的信息是比较明显的。

我的建议是在做了很多分配的Java里面的分配和设计功能,可能增长数组以两种模式操作计划:一个是指大小需要和一个实际做的工作。你可以做到这一点与

  ssize_t供some_function(字符*中,为size_t in_sz){
  如果(in_sz&LT; the_size_I_need){
    返回the_size_I_need; //查询尺寸为pretty快
  }  //做一些工作,如果它足够大  //使用负尺寸或例外,以指示错误  返回the_size_I_really_used; //发送实际尺寸回Java
}

这将允许你做类似Java中的以下内容:

  INT SZ = module.some_function(新的字节[0]);
字节的结果[] =新的字节[深圳]
SZ = module.some_function(结果);

请注意,与默认typemaps的新的字节[0] 需要,因为他们不允许用作阵列 - 你可以添加typemaps,使这个,如果你想要的,或者使用%延长来提供并不需要一个空数组过载

I'm a bit lost with typemaps in swig and how to use arrays. I have prepared a working example that uses arrays between java and c using swig, but i don't know if it is the correct way to do it.

Basically i want to pass a byte array byte[] from java to c as a ´signed char *` + it's size, modify it in c and see the changes in java and create an array in c and use it in Java.

I have take a look at theese questions: 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?

And in fact used the solutions as a guide to make the example.

This is my code in the file 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;
}

This is the .i file for swig (arrays.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"

And finally the Java code to test it:

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();
    }
}

The example works, but I'm not sure that that is the correct way, i mean:

is there an easier way to achieve the same result?

does this code have memory leaks (on one hand i think there is because i do a calloc but i don't free it, but on the other hand i pass it to the SetByteArrayRegion, so maybe freeing it would cause an error)?

does the SetByteArrayRegion copy the values or only the reference?, for example if instead of actually doing a calloc what if obtaining an array from an c++ object by reference that is going to be destroy when it exits scope?

is the array returned to Java correctly freed when nullifying it?

is there a way to specify from where to where a typemap applies?, i mean, in the .i code i have provided a typemap for each function, where i think i could reuse some of them, but if there were others functions with same parameters that i don't want to typemap them, how can i do that, i may not be able to modify the parameters name of the functions.

I have seen the carrays.i possibility described in this question How do I pass arrays from Java to C++ using Swig?, but that implies that if the size of the array is 1000 items and i want to send it through a Java Socket or create a String from it, i have to make 1 JNI Call for each array item. And i actually want a byte[] in the Java side, not a set of functions to access the underlaying array, so already existing code works without modifications.


Context: The reason i want to achieve this is that there is a library that have some functionality, but the important part here is that it allows to import and export data from the library making use of the Google Protocols Buffers. So the code related to this question looks like this:

class SomeLibrary {

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

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

}

The thing is that Protobuf in C++ uses std::string to store the data, but this data is binary so it can not be returned as a normal Java String because it gets truncated, more of this in Swig: convert return type std::string(binary) to java byte[].

So my idea is to return to Java a byte[] for the serialized Protobuf (as does the Java version of Protocol buffers) and accept byte[] for parsing protobufs. To avoid getting SWIGTYPE_p_std_string in the second argument of the export, and having String for the second argument of import y have wrapped both functions using %extend, like this:

%extend SomeLibrary{

  bool export(const std::string & sName, char ** toExportData, int * toExportLength);

  bool import(const std::string & sName, char * toImportData, int toImportLength);

}

And now i should be able to make the typemaps.

But in order to be more general, i asked for the general of manipulating arrays from Java to SWIG, having the native Java byte[].

解决方案

Don't discount carrays.i automatically. That said SWIG has some convenient typemaps already:

%module test

%apply(char *STRING, size_t LENGTH) { (char *str, size_t len) };

%inline %{
void some_func(char *str, size_t len) {
}
%}

Which produces a function in the Java interface:

public static void some_func(byte[] str)

i.e. it takes an array you can build in Java like normal and fills in the pointer and length for you. Almost for free.

Your code as it stands almost certainly leaks - you'd want to call free() within the argout typemap to release the memory you allocated once it's been copied into the new Java array.

You can selectively apply typemaps by both the type and the name of the parameters. See this document for more on typemap matching rules. You can also request to explicitly use a typemap where it wouldn't otherwise be used with %apply as in the example I showed above. (Actually it copies the typemaps, so that if you modified just one of them it doesn't replace it in the general case)

In general the typemaps for passing arrays from Java to C++ or working with arrays of known size are simpler than ones for returning from C++ to Java because the size information is more obvious.

My suggestion would be to plan on doing a lot of the allocation inside Java the allocation and designing your functions that might grow an array to operate in two modes: one that indicates the size needed and one that actually does the work. You might do that with:

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
}

That would allow you to do something like the following in Java:

int sz = module.some_function(new byte[0]);
byte result[] = new byte[sz];
sz = module.some_function(result);

Note that with the default typemaps the new byte[0] is needed because they don't allow null to be used as an array - you could add typemaps that allow this if you wanted, or use %extend to provide an overload that didn't need an empty array.

这篇关于正确的方法使用痛饮阵列互动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆