简体   繁体   English

Swig:将返回类型std :: string(binary)转换为java byte []

[英]Swig: convert return type std::string(binary) to java byte[]

My situation is that i have a C++ class (MyClass) with a method that has the following signature: 我的情况是我有一个C ++类(MyClass),其方法具有以下签名:

bool getSerialized(const stdString & name, std::string & serialized);

Where name is a in argument and serialized is an out argument. 其中name是一个in参数,而序列化的是out参数。

I got it working by making a %extend and %ignore declarations in the 'i' file as follows: 我通过在'i'文件中进行%extend和%ignore声明来使其工作,如下所示:

%extend MyClass{
    std::string getSerialized(const std::string & name){
        std::string res;
        $self->getSerialized(name, res);
        return res;
};
%rename("$ignore", fullname=1) "MyClass::getSerialized";

So the method con be used from Java like: 因此可以从Java使用以下方法:

MyClass mc = new MyClass();
String res = mc.getSerialized("test");

But now i have encountered a problem, the serialized std::string contains binary data, including the '\\0' character witch indicates the end of a C String, in fact the following code shows the problem in C++: 但是现在我遇到了一个问题,序列化的std :: string包含二进制数据,其中包括'\\ 0'字符,女巫表示C字符串的结尾,实际上以下代码显示了C ++中的问题:

std::string s;
s.push_back('H');
s.push_back('o');
s.push_back(0);
s.push_back('l');
s.push_back('a');
std::cout << "Length of std::string " << s.size() << std::endl;
std::cout << "CString: '" << s.c_str() << "'" << std::endl;

The code above displays: 上面的代码显示:

Length of std::string 5
CString: 'Ho'

As i have seen in the wrap file generated by SWIG, the wrap method actually calls c_str(), code of wrap: 正如我在SWIG生成的包装文件中看到的那样,wrap方法实际上调用c_str(),即包装代码:

jstring jresult = 0 ;
std::string result;
result = (arg1)->getSerialized();
jresult = jenv->NewStringUTF((&result)->**c_str()**); 
return jresult;

So as expected the received String in Java gets truncated. 因此,正如预期的那样,Java中收到的String被截断了。 So how can i change (presumably) my %extend function wrapper so i can return this as a byte array (byte[]), without previously knowing the length of the array. 因此,如何更改(大概是)我的%extend函数包装,以便我可以将其作为字节数组(byte [])返回,而无需事先知道数组的长度。 It would be great if the byteArray could be created in the SWIG layer, so i could invoke the method from Java like: 如果可以在SWIG层中创建byteArray,那就太好了,所以我可以从Java调用该方法,例如:

byte[] serialized = mc.getSerialized("test");

Other considerations: The use of std::string for storing binary data is given, as is the returned type that uses the Google protobuf library C++ protobuf usage 其他注意事项:给出了使用std :: string来存储二进制数据的方法,以及使用Google protobuf库C ++ protobuf用法的返回类型。

There is a very similar question, including the tittle Swig: convert return type std::string to java byte[] but there is no case for binary data, so the solution given there doesn't apply here. 有一个非常相似的问题,包括标题Swig:将返回类型std :: string转换为java byte [],但是没有二进制数据的情况,因此此处给出的解决方案不适用。

Using SWIG 2. 使用SWIG 2。

You can do what you're trying to do with a few typemaps and some JNI. 您可以使用一些类型映射和一些JNI来完成您想做的事情。 I put together an example: 我举了一个例子:

%module test

%include <std_string.i>

%typemap(jtype) bool foo "byte[]"
%typemap(jstype) bool foo "byte[]"
%typemap(jni) bool foo "jbyteArray"
%typemap(javaout) bool foo { return $jnicall; }
%typemap(in, numinputs=0) std::string& out (std::string temp) "$1=&temp;"
%typemap(argout) std::string& out {
  $result = JCALL1(NewByteArray, jenv, $1->size());
  JCALL4(SetByteArrayRegion, jenv, $result, 0, $1->size(), (const jbyte*)$1->c_str());
}
// Optional: return NULL if the function returned false
%typemap(out) bool foo {
  if (!$1) {
    return NULL;
  }
}

%inline %{
struct Bar {
  bool foo(std::string& out) {
    std::string s;
    s.push_back('H');
    s.push_back('o');
    s.push_back(0);
    s.push_back('l');
    s.push_back('a');
    out = s;
    return true;
  }
};
%}

It states that the C++ wrapper with return a Java byte array for the functions that match bool foo . 它指出C ++包装器针对匹配bool foo的函数返回一个Java字节数组。 It also sets up a temporary std::string to give to the real implementation of foo that hides the input parameter from the Java interface itself. 它还设置了一个临时的std::string来提供foo的真实实现,该实现从Java接口本身隐藏了输入参数。

Once the call has been made it creates and returns a byte array provided the function didn't return false. 调用完成后,它会创建并返回一个字节数组,前提是该函数未返回false。

I checked that it all worked as expected with: 我检查了以下各项是否都按预期工作:

public class run { 
  public static void main(String[] argv) {
    String s = "ho\0la";
    System.out.println(s.getBytes().length);

    System.loadLibrary("test");
    Bar b = new Bar();
    byte[] bytes = b.foo();
    s = new String(bytes);
    System.out.println(s + " - " + s.length());
    assert(s.charAt(2) == 0);
  }
}

You should be aware of the implications of the cast to const jbyte* from the return type of c_str() - it may not always be what you wanted. 您应该意识到从c_str()返回类型const jbyte*const jbyte*的含义-可能并不总是您想要的。

As an alternative if the size of the output byte array was actually fixed or trivially predictable you could pass that in pre-allocated as the input to begin with. 或者,如果输出字节数组的大小实际上是固定的或微不足道的,则可以将其预先分配为输入。 This would work because arrays are effectively passed by reference into functions in the first place. 这将起作用,因为首先要通过引用将数组有效地传递给函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM