4

私の状況では、次のシグネチャを持つメソッドを持つ 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";

したがって、メソッド con は Java から次のように使用できます。

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

しかし、今、私は問題に遭遇しました。シリアル化された std::string には、C 文字列の終わりを示す '\0' 文字魔女を含むバイナリ データが含まれています。実際、次のコードは 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 によって生成されたラップ ファイルで見たように、ラップ メソッドは実際にはラップのコード c_str() を呼び出します。

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

したがって、予想どおり、Java で受信した文字列は切り捨てられます。では、配列の長さを事前に知らなくても、これをバイト配列 (byte[]) として返すことができるように、(おそらく) %extend 関数ラッパーを変更するにはどうすればよいでしょうか。SWIG レイヤーで byteArray を作成できれば素晴らしいので、次のように Java からメソッドを呼び出すことができます。

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

その他の考慮事項: バイナリ データを格納するための std::string の使用が指定されており、Google の protobuf ライブラリを使用する返される型と同様に、C++ protobuf の使用法が示されています。

タイトル Swig : convert return type std::string to java byte[]を含む非常によく似た質問がありますが、バイナリデータのケースがないため、ここで与えられた解決策は適用されません。

SWIG2を使用。

4

1 に答える 1

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++ラッパーがに一致する関数のJavaバイト配列を返すことを示していますbool foo。また、Javaインターフェース自体から入力パラメーターを隠すstd::string実際の実装に与える一時的な設定も行います。foo

呼び出しが行われると、関数が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()-それは必ずしもあなたが望むものではないかもしれません。

別の方法として、出力バイト配列のサイズが実際に固定されているか、簡単に予測できる場合は、最初に入力として事前に割り当てられたサイズを渡すことができます。そもそも配列は参照によって関数に効果的に渡されるため、これは機能します。

于 2012-08-30T12:08:15.043 に答える