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

标签 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 文件中看到的,wrap 方法实际上调用了 c_str(),wrap 代码:

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

因此,正如预期的那样,Java 中接收到的字符串被截断了。那么我该如何更改(大概)我的 %extend 函数包装器,以便我可以将其作为字节数组 (byte[]) 返回,而无需事先知道数组的长度。如果可以在 SWIG 层中创建 byteArray 就好了,这样我就可以从 Java 中调用该方法,例如:

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

其他注意事项: 给出了使用 std::string 存储二进制数据,以及使用 Google protobuf 库的返回类型 C++ protobuf usage

有一个非常相似的问题,包括标题Swig: convert return type std::string to 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;
  }
};
%}

它声明 C++ 包装器为匹配 bool foo 的函数返回一个 Java 字节数组。它还设置了一个临时的 std::string 以提供 foo 的真正实现,它从 Java 接口(interface)本身隐藏了输入参数。

调用完成后,它会创建并返回一个字节数组,前提是该函数未返回 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);
  }
}

您应该了解从 c_str() 的返回类型转换为 const jbyte* 的含义 - 它可能并不总是您想要的。

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

关于java - Swig:将返回类型 std::string(binary) 转换为 java byte[],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12192624/

相关文章:

python - 使用 SWIG 将 C++ 文件包装为 Python 文件

Java - 询问用户是否要继续(仅接受 'y' 或 'n' )

java - 在java中登录多线程应用程序

c - 使用 CMake 生成 SWIG 绑定(bind)

python - 将 SWIG 与指向 C 结构中的函数的指针一起使用

c++ - SWIG、CPP 和 Perl

java - 如何使用rxJava正确调用retrofit服务?

Java Selenium 3.3.1 和 geckodriver 0.15.0 不在 Windows 10 上等待

java - Lambda 表达式在一个接口(interface)上工作吗?

java - SWIG java : releasing memory allocated in c++