2011-11-16 73 views
11

我有一些本機C++代碼,我正在使用SWIG轉換爲Java,以便我的Java應用程序可以使用它。特別是有一些函數返回std :: vector。這是我的接口文件的一個片段:SWIG(v1.3.29)生成的C++到Java Vector類不能正常工作

%include "std_vector.i" 
namespace std { 
    %template(Vector) vector<double>; 
    %template(Matrix) vector<vector<double> >; 
} 

%include "std_string.i" 

std_string.istd_vector.i被列入我痛飲的構建我使用。我首先感到驚訝的是,Java輸出包含了SWIG的Vector類的「自己」版本(與使用java.util.Vector相反)。我真正的問題是,從這些函數返回的向量似乎不工作。例如,我無法使用get()(有時會使程序崩潰)或返回負值的size()函數檢索其內容。我知道Vector包含數據,因爲我編碼的'String'版本的相同函數只是遍歷Vector(返回本機C++代碼),並以逗號分隔的String值返回內容。雖然這是一個有效的解決方法,但最終我希望這能夠正常工作,使我能夠接收和操作Vectors。任何幫助/提示將不勝感激。

+0

不是SWIG用戶,但是看着'std_vector.i'(我在網上找到它的版本,無論如何),'size()'應該是'unsigned int',SWIG應該將到Java'long'。如果你的尺寸是負值,是純粹的廢話,還是看起來像他們可能會把'unsigned'當作已簽名的? –

回答

14

用Java打包std::vector的適當基本類型是java.util.AbstractList。使用java.util.Vector作爲基礎會很奇怪,因爲您最終會得到兩組存儲空間,一個存儲在std::vector中,一個存儲在java.util.Vector中。

原因呷不爲你做的,雖然這是因爲you can't have AbstractList<double> in Java,它必須是AbstractList<Double>(從ObjectDouble繼承而double是基本類型)。

說完了我已經放在一起的一個小例子,它很好地用Java包裝了std::vector<double>std::vector<std::vector<double> >。它並不完整,但它支持Java中的「for each」風格的迭代和元素上的「迭代」風格。它應該足以展示如何實現其他事情/當你想要他們。

我會在接下來的章節中討論接口文件,但基本上它們都是順序和完整的。

num.i定義我們的模塊num開始:

%module num 

%{ 
#include <vector> 
#include <stdexcept> 

std::vector<double> testVec() { 
    return std::vector<double>(10,1.0); 
} 

std::vector<std::vector<double> > testMat() { 
    return std::vector<std::vector<double> >(10, testVec()); 
} 
%} 

%pragma(java) jniclasscode=%{ 
    static { 
    try { 
     System.loadLibrary("num"); 
    } catch (UnsatisfiedLinkError e) { 
     System.err.println("Native code library failed to load. \n" + e); 
     System.exit(1); 
    } 
    } 
%} 

我們#include S代表的功能產生num_wrap.cxx和兩個實現測試(他們可能是在一個單獨的文件,我只是把他們在這裏出懶惰/便利)。

還有一個技巧,我喜歡在Java SWIG接口中使用%pragma(java) jniclasscode=,以便爲接口的用戶透明地加載共享對象/ DLL。

接下來在接口文件中是我們想包裝的部分std::vector。我沒有使用std_vector.i因爲我們需要做一些修改:

namespace std { 

    template<class T> class vector { 
     public: 
     typedef size_t size_type; 
     typedef T value_type; 
     typedef const value_type& const_reference; 
     %rename(size_impl) size; 
     vector(); 
     vector(size_type n); 
     size_type size() const; 
     size_type capacity() const; 
     void reserve(size_type n); 
     %rename(isEmpty) empty; 
     bool empty() const; 
     void clear(); 
     void push_back(const value_type& x); 
     %extend { 
      const_reference get_impl(int i) throw (std::out_of_range) { 
       // at will throw if needed, swig will handle 
       return self->at(i); 
      } 
      void set_impl(int i, const value_type& val) throw (std::out_of_range) { 
       // at can throw 
       self->at(i) = val; 
      } 
     } 
    }; 
} 

這裏的主要變化是%rename(size_impl) size;,它告訴夜風從std::vector暴露size()size_impl代替。我們需要這樣做,因爲Java預計size返回int,其中std::vector版本返回的size_type很可能不會是int

接下來在接口文件中,我們告訴它我們想要的基類和接口來實現,以及編寫一些額外的Java代碼來強制功能之間的事情不兼容類型:

%typemap(javabase) std::vector<double> "java.util.AbstractList<Double>" 
%typemap(javainterface) std::vector<double> "java.util.RandomAccess" 
%typemap(javacode) std::vector<double> %{ 
    public Double get(int idx) { 
    return get_impl(idx); 
    } 
    public int size() { 
    return (int)size_impl(); 
    } 
    public Double set(int idx, Double d) { 
    Double old = get_impl(idx); 
    set_impl(idx, d.doubleValue()); 
    return old; 
    } 

%} 

%typemap(javabase) std::vector<std::vector<double> > "java.util.AbstractList<Vector>" 
%typemap(javainterface) std::vector<std::vector<double> > "java.util.RandomAccess" 
%typemap(javacode) std::vector<std::vector<double> > %{ 
    public Vector get(int idx) { 
    return get_impl(idx); 
    } 
    public int size() { 
    return (int)size_impl(); 
    } 
    public Vector set(int idx, Vector v) { 
    Vector old = get_impl(idx); 
    set_impl(idx, v); 
    return old; 
    } 

%} 

此設置基準java.util.AbstractList<Double>的類別爲std::vector<double>java.util.AbstractList<Vector>std::vector<std::vector<double> >Vector是我們將在界面的Java端調用std::vector<double>)。

我們還提供在Java端的getset的實現,它可以處理doubleDouble轉換並返回。

最後在接口我們添加:

namespace std { 
    %template(Vector) std::vector<double>; 
    %template(Matrix) std::vector<vector<double> >; 
} 

std::vector<double> testVec(); 
std::vector<std::vector<double> > testMat(); 

這告訴夜風指std::vector<double>(與特定類型)爲Vector同樣地,對於std::vector<vector<double> >Matrix。我們還告訴SWIG公開我們的兩個測試功能。

接下來,test.java,一個簡單的main在Java中行使我們的代碼一點:

import java.util.AbstractList; 

public class test { 
    public static void main(String[] argv) { 
    Vector v = num.testVec(); 
    AbstractList<Double> l = v; 
    for (Double d: l) { 
     System.out.println(d); 
    } 
    Matrix m = num.testMat(); 
    m.get(5).set(5, new Double(5.0)); 
    for (Vector col: m) { 
     for (Double d: col) { 
     System.out.print(d + " "); 
     } 
     System.out.println(); 
    } 
    } 
} 

要構建和運行這個我們做的:

swig -java -c++ num.i 
g++ -Wall -Wextra num_wrap.cxx -shared -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux/ -o libnum.so 

javac test.java && LD_LIBRARY_PATH=. java test 

我使用g ++ 4.4版本測試了這個和Linux/x86上的SWIG 1.3.40。

num.i的完整版本可以找到here,但始終可以通過將每個部分粘貼到一個文件中從此答案重建。

事情我已經不AbstractList實施:

  1. add() - 可以通過push_back()實現,std_vector.i甚至試圖落實默認兼容的東西,但它不與Double VS double工作問題或匹配AbstractList指定的返回類型(不要忘了增加modCount
  2. remove() - 不太適合std::vector的時間複雜度,但並非不可能實現或者(與同樣)
  3. 推薦使用另一個Collection的構造函數,但在此處未實現。可以在相同的地方執行set()get(),但將需要$javaclassname正確命名生成的構造函數。
  4. 您可能想要使用類似this的內容來檢查中的size_typeint轉換是否理智。
+0

這是相當不錯,真的令人印象深刻,雖然我希望深入瞭解崩潰的問題。我看到在32位體系結構上穩定運行的應用程序的Linux64版本上發生崩潰,我想知道std_vector.i本身可能存在的潛在錯誤。 –

+0

@ ErnestFriedman-Hill - 我無法在我的SWIG版本的'std_vector.i'中看到任何明顯的東西,這會導致此類行爲。我的猜測可能是原生Java類型與封裝C++其他地方的某種類型之間的錯誤映射,或者可能是其他地方更普遍的問題。儘管我害怕,但我無法真正做出超出這個預想的猜測。儘管如此,我將在Linux/x86_64機器上測試它。我希望我所寫的對於那些使用SWIG封裝Java的C++容器的人來說是有用的。 – Flexo

+0

@ ErnestFriedman-Hill - 我無法重現你所報告的問題 - 你能爲它做一個最簡單的工作例子嗎?一個能夠更清楚地顯示崩潰問題的小模塊將非常有幫助。 – Flexo

4

我是在這個問題上提供賞金的人,因爲我有同樣的問題。我有點尷尬地報告說我終於找到了真正的解決方案 - 這是在SWIG手冊中!解決方法是在編譯生成的代碼時使用-fno-strict-aliasing標誌作爲g++ - 這很簡單。我不願意承認,它花了很多谷歌搜索終於找到了這一點。

的問題是,最近的g++版本做了一些積極的優化,使有關指針別名假設不成立的代碼SWIG生成std_vector(和在其他情況下。)g++ 4.1並沒有這樣做,而是4.4.5肯定會。這些假設是完全有效的,並且被當前的ISO標準所允許,儘管我不確定它們的知名度。基本上,這是指不同類型的兩個指針(有幾個例外)可以從決不指定指向相同的地址。 SWIG生成的用於在指針對象和jlong之間轉換的代碼與此規則相違背。