2011-09-22 62 views
1

我有一個C++庫xyz。它有很多類,如xyzA,xyzB等我想要使用類xyzA在xyz庫中的方法getAge()使用JNI從C++庫加載特定類的方法

xyz.so文件已存在。

步驟我都遵循:

  1. 創建一個Java類xyz.java

    class xyz { 
    
        public native int getAge(); 
    
        public static void main(String[] args) { 
         new xyz().getAge(); 
        } 
        static { 
         System.loadLibrary("xyz"); 
        } 
    } 
    
  2. 創建的頭爲Java類。

    /* DO NOT EDIT THIS FILE - it is machine generated */ 
    #include <jni.h> 
    /* Header for class xyz */ 
    
    #ifndef _Included_xyz 
    #define _Included_xyz 
    #ifdef __cplusplus 
    extern "C" { 
    #endif 
    /* 
    * Class:  xyz 
    * Method: getAge 
    * Signature:()I 
    */ 
    JNIEXPORT jint JNICALL Java_xyz_getAge 
        (JNIEnv *, jobject); 
    
    #ifdef __cplusplus 
    } 
    #endif 
    #endif 
    
  3. CPP的包裝類的樣子:

    #include <stdio.h> 
    #include "xyz.h" 
    #include <jni.h> 
    
    JNIEXPORT jint JNICALL Java_xyz_getAge(JNIEnv *, jobject) 
    { 
        // some code 
    } 
    
  4. 我成功編譯的類,如下所示:

    gcc -fPIC -shared -l stdc++ -I/grid/0/gs/java/jdk64/current/include -I/grid/0/gs/java/jdk64/current/include/linux xyz.cpp 
    
  5. 然後運行Java PROG爲:

    java -Djava.library.path=/grid/0/tmp/direct/lib xyz 
    

    我得到以下錯誤:

    Exception in thread "main" java.lang.UnsatisfiedLinkError: xyz.getAge()I 
         at xyz.getAge(Native Method) 
         at xyz.main(xyz.java:6) 
    

它無法找到特定的類xyzA方法getAge()。如何訪問該方法?另外,圖書館是通過我的包裝類來鏈接的嗎?

任何指針,將不勝感激。

謝謝。

回答

2

如果您在Unix上運行,共享庫必須命名爲libxyz.so而不是xyz.so

+0

另一種方法是使用'System.load(String)',並提供完整的路徑和文件名。但是,遵守共享庫命名的平臺慣例仍然是一個好主意。 –

+0

'strace'可以方便地找出jvm試圖加載的內容。 – msandiford

0

C++庫中導出的函數名稱是mangled:普通函數名稱使用類名稱空間名稱,參數和返回類型進行修飾。例如,下面是來自增強庫的方法之一:

 [email protected][email protected]_WV?$alloca 
[email protected][email protected][email protected]@@@[email protected]@[email protected][email protected][email protected] 
[email protected]@@[email protected]@@[email protected]@@[email protected]@@@Z 

名稱的格式不是標準化的,因編譯器而異。最終的結果是,除非您使用C++編寫另一個模塊並使用相同的編譯器編譯它,否則很難調用導出的C++成員函數。試圖從Java調用比其值得的更麻煩。

相反,如果C++庫是你的,使用extern "C"調用約定出口輔助功能:

Foo * foo; 

extern "C" 
{ 
    void myfunc() 
    { 
    foo->bar(); // Call C++ function within a C-style function. 
    } 
} 

如果庫是第三方的,你應該用自己的圖書館使用它公開必要的功能把它包C風格的出口,如上所述。

+1

看看頭文件:'extern「C」'東西已經在那裏了。 –

+0

哎呦。是的,我現在看到了。 – RobH

0

此錯誤通常意味着該庫已成功加載,但該函數的簽名中存在不一致。 在全球範圍內,您的代碼看起來是正確的,但我們確實在的JNIEXPORT前面放置了extern "C"在我們的代碼庫中。我們不使用生成的 標題,因此函數的定義是我們唯一可以指定的 extern "C"。在你的情況下,我認爲編譯器是 應該認識到在頭文件中聲明的函數和你的.cpp中定義的函數是相同的,並且定義函數爲 extern "C",但從未這樣做過,I我不是100%肯定的。您 可以通過執行類似驗證:

nm -C libxyz.so | egrep getAge 

功能應顯示爲C函數,在第二 列噸;沒有-C,它應該顯得沒有問題。注意 在定義和聲明中稍有不同就意味着 你定義了一個不同的函數;我沒有在您發佈的代碼 中看到一個,但值得仔細檢查。

(我還包裹在一個try塊LoadLibrary調用,只是可以肯定的。)

編輯以添加一些額外的信息:

我不知道何時以及如何Linux的鏈接需要額外的庫; 我們一直都明確加載了我們所有的庫。您可以嘗試 在JNI_OnLoad或 靜態對象的構造函數中添加對dlopen的調用。 (我會推薦這個,爲了控制 參數到dlopen我們發現,當加載幾個不同的 .so時,如果我們不這樣做,RTTI不會跨越庫邊界工作。)I 預計不會這樣做導致加載器錯誤,但是這一切都取決於Linux試圖加載的時間libxyz.so;如果只有在 JVM調用dlsym時纔會這樣,那麼您將得到上述錯誤。 (我覺得這個 依賴於JVM,當你調用 java.lang.System.LoadLibrary傳遞給dlopen的參數:如果通過RTLD_LAZYRTLD_NOW 通過迫使JNI_OnLoad負載或靜態 對象的構造函數,你或多或少保證裝載機錯誤出現爲加載器 錯誤,稍後會出現鏈接錯誤。)