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 Type Size Java Type Common Windows Types
char 8-bit integer byte BYTE, TCHAR
short 16-bit integer short WORD
wchar_t 16/32-bit character char TCHAR
int 32-bit integer int DWORD
int boolean value boolean BOOL
long 32/64-bit integer NativeLong LONG
long long 64-bit integer long __int64
float 32-bit FP float  
double 64-bit FP double  
char* C string String LPTCSTR
void* pointer Pointer LPVOID, HANDLE, LPXXX

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

 

https://blog.csdn.net/baidu_35679960/article/details/77585107

posted @ 2019-06-25 18:17  Bigben  阅读(2439)  评论(0编辑  收藏  举报