Linux系统上java调用C++ so库文件

位置独立代码

 

 
PART1:
    java中使用jna替代jni调用c++/c生成的 dll/so库文件需要做的事项

1、引入JNA依赖或者直接下载JNAjar包
 
                    <dependency>
                        <groupId>net.java.dev.jna</groupId>
                        <artifactId>jna</artifactId>
                        <version>5.2.0</version>
                    </dependency>
 
2、编写Java 调用类
 
           
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
     package  com.tree.go.util;
         import  com.sun.jna.Library;
         import  com.sun.jna.Native;
 
         //继承Library,用于加载库文件 --Class mapping
         public   interface  CPPTest   extends  Library {
             // 加载libhello.so链接库
               public  static  final  String JNA_ImgProcess =  "hello" ;
               public  static  final  CPPTest instance = (CPPTest)Native.loadLibrary(CPPTest.JNA_ImgProcess,CPPTest. class );
 
             // 此方法为链接库中的方法  function mapping
             void  test();
             int  addTest( int  a, int  b);
          
         //调用,singleton
         public  static  void  main(String[] args) {
               CPPTest instance =CPPTest.instance;
               instance.test();
               int  c =instance.addTest( 10 , 20 );  
         }
}    

  

 
 
            
接下来的工作就是如何编写可供调用的Cpp文件,以及编译加载的问题了,查看part 2  

 
 
PART2:
    编写C++/C文件,编译

 
准备编写C++代码[T1.cpp],如下:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream> 
  using  namespace  std;
  
  extern  "C" //避免name mangling,编译后名称symbol破坏,导致无法找到函数,告诉编译器下面的代码块使用c编译器来编译
        int  addTest( int  a,  int  b)
{
        cout <<  "a+b"  << a + b << endl;
        return  a + b;
}
        void  test()
        {
              cout <<  "hello word from C++ ! "  << endl;
}
}

  

 
 
1、如何将C++文件编译为so文件?
 
    这里需要区分编译c文件和c++文件使用的是不同的编译器,具体编译参数可以复用
 
        1.1、编译c文件使用 gcc
    示例:
    使用命令:gcc -fPIC -shared -o libGoT.so  T1.c
 
 
        1.2、编译c++文件使用的是 g++
                                g++ -fPIC -shared -o libhello.so  T1.cpp
 
        编译完成后生成如下文件:
                    
 
2、如何加载编译好的so文件?
 
         配置so文件加载位置:
打开  vim /etc/profile
添加如下配置:
/home/data/libso是自定义目录,
export LD_LIBRARY_PATH=/home/data/libso
多个目录用:隔开,如下
export LD_LIBRARY_PATH=/home/data/libso:/usr/lib
 
这样就可在Java中调用使用C程序编写好的代码了

 
 
PART3:
    注意事项:

1、C++编译后函数名称破环问题
        名称一致性问题,java中调用的和cpp文件中定义的名称需要保持一致
        解决:使用   extern "C"放到一句代码前,或者一段代码前 extern "C"{ your code}
    
 
2、   编译生成动态库名的问题
            注意在编译的时候一点要在库民前面加上  lib+soname.so
            否则JNA如法加载到库文件
            示例:我们需要一个hello库需要这样编译,前面加上lib
 
                 g++ -fPIC -shared -o libhello.so  T1.cpp
                    
        

linux动态库的命名规则

动态链接库的名字形式为 libxxx.so,前缀是lib,后缀名为“.so”。
  针对于实际库文件,每个共享库都有个特殊的名字“soname”。在程序启动后,程序通过这个名字来告诉动态加载器该载入哪个共享库。
  在文件系统中,soname仅是一个链接到实际动态库的链接。对于动态库而言,每个库实际上都有另一个名字给编译器来用。它是一个指向实际库镜像文件的链接文件(lib+soname+.so)。
 
 
 

显式调用C++动态库注意点

对C++来说,情况稍微复杂。显式加载一个C++动态库的困难一部分是因为C++的name mangling;另一部分是因为没有提供一个合适的API来装载类,在C++中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。
name mangling可以通过extern "C"解决。C++有个特定的关键字用来声明采用C binding的函数:extern "C" 。用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。因此,只有非成员函数才能被声明为extern "C",并且不能被重载。尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了,相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。
另外如何从C++动态库中获取类,附上几篇相关文章,但我并不建议这么做:
l  《LoadLibrary调用DLL中的Class》: http://www.cppblog.com/codejie/archive/2009/09/24/97141.html
“显式”使用C++动态库中的Class是非常繁琐和危险的事情,因此能用“隐式”就不要用“显式”,能静态就不要用动态。

附件:Linux下库相关命令

g++(gcc)编译选项

  -shared :指定生成动态链接库。
  -static :指定生成静态链接库。
  -fPIC :表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码,念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。
  -L. :表示要连接的库所在的目录。
  -l:指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。
  -Wall :生成所有警告信息。
  -ggdb :此选项将尽可能的生成gdb 的可以使用的调试信息。
  -g :编译器在编译的时候产生调试信息。
  -c :只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件) 。
  -Wl,options :把参数(options)传递给链接器ld 。如果options 中间有逗号,就将options分成多个选项,然后传递给链接程序。

nm命令

有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:
  一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;
  一种是库中定义的函数,用T表示,这是最常见的;
  一种是所谓的弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。
$nm libhello.h

ldd命令

    ldd命令可以查看一个可执行程序依赖的共享库,例如我们编写的测试动态库依赖下面这些库:
        
 
 

echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/haihang/linux64" >> ~/.bashrc
source ~/.bashrc

多个源文件相互调用,然后才调用动态库
gcc  -shared -Wl,-soname, libtest.so -o libtest.so subNodeList.o NodeList.o ParseXml.o bm.o distance.o   MatchKey.o TraitList.o Tree.o  UpdateRule.o detect.o test.o -lm  -lxml2

g++ -fPIC -shared -o libhello.so hello.cpp hsdownload.cpp -L./ -lpthread -lstdc++ -lrt -ldl -lm -lresolv -lcrypto -lssl -lcurl -lprotobuf -lhpr -lhlog -lopensslwrap -lys_net -lezviz_streamclient -lezserveropensdk -I./include -I./
# nm libhello.so |grep hs_download


 

Default Type Mappings

Java primitive types (and their object equivalents) map directly to the native C type of the same size.

Native TypeSizeJava TypeCommon Windows Types
char8-bit integerbyteBYTE, TCHAR
short16-bit integershortWORD
wchar_t16/32-bit charactercharTCHAR
int32-bit integerintDWORD
intboolean valuebooleanBOOL
long32/64-bit integerNativeLongLONG
long long64-bit integerlong__int64
float32-bit FPfloat 
double64-bit FPdouble 
char*C stringStringLPTCSTR
void*pointerPointerLPVOID, HANDLE, LPXXX

Unsigned types use the same mappings as signed types. C enums are usually interchangeable with "int".

 

https://blog.youkuaiyun.com/baidu_35679960/article/details/77585107

转载于:https://www.cnblogs.com/bigben0123/articles/11084523.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值