我的情况是我有一个C ++类(MyClass),其方法具有以下签名:
bool getSerialized(const stdString & name, std::string & serialized);
Run Code Online (Sandbox Code Playgroud)
其中name是一个in参数,而序列化的是out参数。
我通过在'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";
Run Code Online (Sandbox Code Playgroud)
因此可以从Java使用以下方法:
MyClass mc = new MyClass();
String res = mc.getSerialized("test");
Run Code Online (Sandbox Code Playgroud)
但是现在我遇到了一个问题,序列化的std :: string包含二进制数据,其中包括'\ 0'字符witch表示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;
Run Code Online (Sandbox Code Playgroud)
上面的代码显示:
Length of std::string 5
CString: 'Ho'
Run Code Online (Sandbox Code Playgroud)
正如我在SWIG生成的包装文件中看到的那样,wrap方法实际上调用c_str(),即包装代码:
jstring jresult = 0 ;
std::string result;
result = (arg1)->getSerialized();
jresult = jenv->NewStringUTF((&result)->**c_str()**);
return jresult;
Run Code Online (Sandbox Code Playgroud)
因此,正如预期的那样,Java中收到的String被截断了。因此,如何更改(大概)我的%extend函数包装器,以便我可以将其作为字节数组(byte [])返回,而无需事先知道数组的长度。如果可以在SWIG层中创建byteArray,那就太好了,所以我可以从Java调用该方法,例如:
byte[] serialized = mc.getSerialized("test");
Run Code Online (Sandbox Code Playgroud)
其他注意事项:给出了使用std :: string来存储二进制数据的方法,以及使用Google protobuf库C ++ protobuf用法的返回类型。
有一个非常相似的问题,包括标题Swig:将返回类型std :: string转换为java byte [],但是没有二进制数据的情况,因此此处给出的解决方案不适用。
使用SWIG 2。
您可以使用一些类型映射和一些JNI来完成您想做的事情。我举了一个例子:
%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;
}
};
%}
Run Code Online (Sandbox Code Playgroud)
它指出C ++包装器将为匹配的函数返回Java字节数组bool foo。它还设置了一个临时std::string实例,以实现foo该实例的真实实现,从而将输入参数从Java接口本身隐藏起来。
调用完成后,它会创建并返回一个字节数组,前提是该函数未返回false。
我检查了以下各项是否都按预期工作:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
您应该意识到const jbyte*返回类型的强制转换的含义c_str()-它可能并不总是您想要的。
或者,如果输出字节数组的大小实际上是固定的或微不足道的,则可以将其预先分配为输入。这将起作用,因为首先要通过引用将数组有效地传递给函数。