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

时间:2012-08-30 08:15:40

标签: java swig

我的情况是我有一个C ++类(MyClass),其方法具有以下签名:

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

其中name是in参数,serialized是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";

所以这个方法可以在Java中使用,如:

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

但是现在我遇到了一个问题,序列化的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;

上面的代码显示:

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

正如我在SWIG生成的包装文件中看到的那样,wrap方法实际上调用c_str(),包装代码:

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

正如预期的那样,Java中收到的String被截断了。那么我怎么能改变(推测)我的%扩展函数包装器,所以我可以将它作为字节数组(byte [])返回,而不需要知道数组的长度。如果可以在SWIG层中创建byteArray会很棒,所以我可以从Java调用方法,如:

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

其他考虑因素: 给出了使用std :: string存储二进制数据的方法,以及使用Google protobuf库的返回类型C++ protobuf usage

有一个非常类似的问题,包括tittle Swig: convert return type std::string to java byte[],但二进制数据没有任何理由,所以这里给出的解决方案不适用于此。

使用SWIG 2。

1 个答案:

答案 0 :(得分:5)

您可以使用一些类型映射和一些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;
  }
};
%}

它指出C ++包装器为匹配bool foo的函数返回一个Java字节数组。它还设置了一个临时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);
  }
}

你应该从const jbyte*的返回类型中了解演员对c_str()的含义 - 它可能并不总是你想要的。

作为替代方案,如果输出字节数组的大小实际上是固定的或平凡可预测的,那么您可以将其作为输入开始预先分配。这可行,因为数组首先通过引用有效地传递给函数。

相关问题