我有一个C ++库xyz。它有很多类,例如xyzA
,xyzB
等。我想使用xyz库中的类getAge()
中的方法xyzA
。
xyz.so
文件已存在。
我遵循的步骤:
创建了一个Java类xyz.java
class xyz {
public native int getAge();
public static void main(String[] args) {
new xyz().getAge();
}
static {
System.loadLibrary("xyz");
}
}
为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
cpp包装器类看起来像:
#include <stdio.h>
#include "xyz.h"
#include <jni.h>
JNIEXPORT jint JNICALL Java_xyz_getAge(JNIEnv *, jobject)
{
// some code
}
我按如下方式成功编译了这个类:
gcc -fPIC -shared -l stdc++ -I/grid/0/gs/java/jdk64/current/include -I/grid/0/gs/java/jdk64/current/include/linux xyz.cpp
然后运行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)
找不到特定于类getAge()
的方法xyzA
。如何访问该方法?另外,库是通过我的包装器类链接的吗?
任何指针都会受到赞赏。
感谢。
答案 0 :(得分:2)
如果您在Unix上运行,则共享库必须命名为libxyz.so
,而不是xyz.so
。
答案 1 :(得分:0)
C ++库中的导出函数名称是 mangled :普通函数名称使用类和命名空间名称,参数和返回类型进行修饰。例如,以下是boost库中的一种方法:
?estimate_max_state_count@?$perl_matcher@PB_WV?$alloca
tor@U?$sub_match@PB_W@boost@@@std@@U?$regex_traits@_WV?$w32_regex_traits@_W@boos
t@@@boost@@@re_detail@boost@@AAEXPAUrandom_access_iterator_tag@std@@@Z
名称的格式不是标准化的,因编译器而异。最终结果是,除非您在C ++中编写另一个模块并使用相同的编译器对其进行编译,否则很难调用导出的C ++成员函数。尝试从Java调用比实现更麻烦。
相反,如果C ++库是您的,请使用extern "C"
调用约定导出帮助函数:
Foo * foo;
extern "C"
{
void myfunc()
{
foo->bar(); // Call C++ function within a C-style function.
}
}
如果库是第三方,您应该使用自己的库包装它,该库使用C风格的导出公开必要的功能,如上所述。
答案 2 :(得分:0)
此错误通常表示库已成功加载,
但是功能的签名存在不一致。
在全球范围内,您的代码看起来是正确的,但我们确实添加了extern "C"
在我们的代码库中JNIEXPORT
的前面。我们不使用生成的
标题,所以函数的定义是我们唯一可以做到的
指定extern "C"
。在你的情况下,我认为编译器是
应该认识到在标题中声明的函数和
在.cpp
中定义的一个是相同的,并将函数定义为
extern "C"
,但从未这样做,我不是百分百肯定。您
可以通过以下方式进行验证:
nm -C libxyz.so | egrep getAge
该函数应显示为C函数,第二个函数为T. 柱;没有-C,它应该看起来没有咒骂。请注意 定义和声明中最微小的差异就意味着 你正在定义一个不同的功能;我在代码中没有看到一个 你发布了,但值得仔细检查。
(我还会在try块中包含对LoadLibrary的调用,只是为了确定。)
已编辑以添加一些其他信息:
我不确定Linux何时以及如何链接所需的其他库;
我们总是明确地加载我们所有的库。你可以试试
在dlopen
或者构造函数中添加对JNI_OnLoad
的调用
静态对象。 (无论如何,我会推荐这个,以便控制
dlopen
的参数。我们发现加载时有几种不同
.so
,如果我们不这样做,RTTI在图书馆边界上不起作用。)I
不希望这样做会导致加载程序错误,但这一切都是如此
取决于Linux何时尝试加载libxyz.so
;如果它只是这样做的话
JVM调用dlsym
,然后你就会得到上面的错误。 (我认为这
取决于JVM在调用时传递给dlopen
的参数
java.lang.System.LoadLibrary
:如果它通过RTLD_LAZY
或RTLD_NOW
。
通过强制JNI_OnLoad
中的加载或静态的构造函数
对象,您或多或少保证加载器错误显示为加载器
错误,而不是链接错误,稍后。)