2012-08-30 12 views
3

我的情況是,我有一個C++類(MyClass的)與具有以下簽名的方法:痛飲:返回類型的std :: string(二進制)轉換成Java字節[]

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

其中name是一個參數,序列化是一個失敗的論點。

我得到了它的製作%的工作延伸和%忽略「我」文件聲明如下:

%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中使用CON,如:

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產生的渦卷文件所看到的,包裝方法實際調用c_str(),渦卷的代碼:

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

因此,作爲預計在Java中收到的字符串會被截斷。所以我怎麼能改變(大概)我的%擴展函數包裝,所以我可以返回這個字節數組(字節[]),而不需要事先知道數組的長度。這將是巨大的,如果字節組可在痛飲層來創建,所以我可以從Java調用的方法,如:

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

其他注意事項: 用於存儲二進制數據給出了使用的std :: string的,正如使用Google protobuf庫的返回類型一樣,有一個非常類似的問題,其中包括tittle Swig: convert return type std::string to java byte[],但沒有二進制數據的情況,所以在此給出的解決方案在此不適用。

使用痛飲2.

+0

輸出參數的長度是否可預測?即你可以知道電話之前的長度嗎? – Flexo

回答

4

你可以做你試圖用幾個typemaps和一些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該功能。它還設置了一個臨時的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); 
    } 
} 

你應該知道從c_str()返回類型投給const jbyte*的含義 - 它不一定是你想要的。

作爲一種替代方案,如果輸出字節數組的大小實際上是固定的或可以預測的,那麼您可以將其作爲輸入以預先分配的形式傳遞給開頭。這是可行的,因爲數組首先通過引用有效地傳遞給函數。

+0

謝謝(再次:-))。它工作完美。只有一條評論,我首先使用沒有'可選'代碼的解決方案,但由於轉換而無法編譯(無法從生成的包裝代碼中將jboolean轉換爲jbyteArray),因此它看起來像畢竟,代碼不是可選的。公寓從那以後,演員對const jbyte *有什麼影響? –

+1

@JavierMr - 如果你在函數返回false時不想返回NULL,你可能需要寫'%typemap(out)bool foo「」''。 (無論是那個還是評論在我發佈的答案中的代碼之前) – Flexo

相關問題