2011-09-22 56 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负载或静态 对象的构造函数,你或多或少保证装载机错误出现为加载器 错误,稍后会出现链接错误。)