C++ - Java 调用 C++ 动态库 <.dylib / .so> By CLion

目录

一.引言

二.CLion

1.inter.h

2.test.cpp

3.编译 .dylib / .so

4.可能遇到的坑

三.IDEA

1.添加 jna 依赖

2.添加 libtest.dylib 到项目

3.Java 项目测试

四.总结


一.引言

下文主要讲解通过 CLion 将 C++ V14 编码为 .dylib 或者 .so 文件并使用 Java 调用 C++ 库示例。

实现上述需求将用到如下组件,使用其他编译器的同学可能会和下述介绍方法有不同:

- CLion

基于 C++ 14标准生成 .dylib 文件或 .so 文件,前者应用于 MacOs 后者应用于 Linux  

- IDEA

基于 Java 1.8 通过 jna 库实现 C++ 动态库的调用。

恰逢卡塔尔世界杯火热进行中,这里也希望总裁能够在这一届世界杯走的更远,取得更好的成绩!

C++ - Java 调用 C++ 动态库 <.dylib / .so> By CLion_第1张图片

二.CLion

1.inter.h

在 .h 头文件中声明三维坐标类 Point,其中包含三个字段 x、y、z 定义坐标点。

// inter.h
#include
struct Point
{
    float x, y, z;
};

Point add(Point p);

2.test.cpp

.cpp 源文件中主要包含两部分内容:

第一部分为 Point add 方法,该方法将 Point 的 x、y、z 坐标均增加 1

// test.cpp
#include
#include"inter.h"

Point add(Point point) {
    point.x += 1;
    point.y += 1;
    point.z += 1;
    return point;
}

第二部分为 __cplusplus,_cplusplus 翻译过来其实就是 C++,其常与 extern "C" 搭配使用,其目的是标记 一部分代码并指示编译器,这部分代码按 C 语言的格式进行编译,而不是 C++ 的。这是使用 C 扩展是因为 C 的函数名不会变,如果使用 C++ 变量名会发生乱码现象,导致我们在 Java 调用时方法名乱码无法调用。

这里 Jna_add 方法很简单,对原始 Point 执行 add +1 操作,最终通过 ans 返回。阅读代码也可以看到,由于 ans 的类型为 Points *,其中 * 号代表指针,所以最后返回的是第一个结构体的指针。如果返回的为一个数组,需要返回长度信息才能输出结果。

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) Point* Jna_add(Point point) {
    Point a = add(point);
    Point* ans = new Point[1];
    for (int i = 0; i < 1; ++i) {
        ans[i].x = a.x;
        ans[i].y = a.y;
        ans[i].z = a.z;
    }
    return ans;
}
#ifdef __cplusplus
}
#endif

3.编译 .dylib / .so

- .dylib For MacOs

dylibs 文件应用于 MacOs 环境下的 Java 调用:

C++ - Java 调用 C++ 动态库 <.dylib / .so> By CLion_第2张图片

执行 Build Project 方法后生成 libXXX.dylib 文件,这里 XXX 与 CMakeLists.txt 中配置相关。由于下面的 Java 测试在 Mac 本机测试,所以博主测试样例使用 .dylib 文件。

- .so For Linux

在项目目录下执行:

g++ test.cpp -fPIC -shared -o libadd.so

-fPIC 表示生成位置无关代码

-shared 表示生成一个动态链接库

动态编译库名称为 libXXX.so,其中 XXX 标识动态库名称。

C++ - Java 调用 C++ 动态库 <.dylib / .so> By CLion_第3张图片

Tips:

Window 环境下为 .dll 文件,如果使用该类型文件大家可自行搜索一下。

4.可能遇到的坑

A.__declspec attributes are not enabled

未启用 __declspec 属性,解决方法也很简单,在 CMakeLists.txt 中添加如下配置重新 ReLoad 配置即可:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fms-extensions")

修改后报错异常消失:

C++ - Java 调用 C++ 动态库 <.dylib / .so> By CLion_第4张图片

B.ninja: no work to do.

C++ - Java 调用 C++ 动态库 <.dylib / .so> By CLion_第5张图片

该异常出现在 Build 项目生成 .dylib 文件时,如果要生成对应文件,需要在 CMakeLists.txt 中添加要编译的信息与 cpp 源文件:

add_library(test SHARED test.cpp)

添加后 Reload 配置即可。

三.IDEA

1.添加 jna 依赖

        
            net.java.dev.jna
            jna
            5.3.1
        

该依赖主要用于 Java 调用 C++ 生成的动态库。

2.添加 libtest.dylib 到项目

因为是测试,这里直接放置到 Java 项目根目录下,除此之外,也可以将文件放置在 src/main/resources/linux-x86-64 、 /usr/lcoal  等多个目录位置,Natice.load 方法会自动寻址。

3.Java 项目测试

这里也分为两部分分解,第一部分为 Jna 结构体,主要实现 C++ Point 类在 Java 中的重新定义,并实现 UserValue 继承 Point 实现静态类,其中元素类型 x、y、z 与之前的 float 对应。

- 通过 JnaLibrary 实现静态库的引入

- Jna_add 定义方法使用方式

    // java
    public interface JnaLibrary extends Library {
        JnaLibrary INSTANCE = Native.load("test", JnaLibrary.class); // 引入 C++ 动态库

        Point Jna_add(Point.ByValue point); // 定义使用方式

        @Structure.FieldOrder({"x", "y", "z"}) // 构建结构体
        class Point extends Structure {

            public Point() {}
            public static class UserValue extends Point implements Structure.ByValue {
                public UserValue(float x, float y, float z) {
                    super(x, y, z);
                }
            }

            public Point(float x, float y, float z) {
                this.x = x;
                this.y = y;
                this.z = z;
            }

            public float x;
            public float y;
            public float z;
        }
    }

第二部分为 main 主函数:

首先初始化一个静态类,随后调用 INSTANCE.Jna_add 方法并打印:

    public static void main(String[] args) {
        JnaLibrary.Point.UserValue startPoint = new JnaLibrary.Point.UserValue(1, 2, 3);
        JnaLibrary.Point a = JnaLibrary.INSTANCE.Jna_add(startPoint);
        
        System.out.println(a.x);
        System.out.println(a.y);
        System.out.println(a.z);
    }

Tips:

这里注意方法名要匹配,jna_add、Jna_Add 等有大小写差异的都会异常报错。

C++ - Java 调用 C++ 动态库 <.dylib / .so> By CLion_第6张图片

运行后得到下述结果代表调用成功:

C++ - Java 调用 C++ 动态库 <.dylib / .so> By CLion_第7张图片

四.总结

上述方法实现了在 Java 中调用 C++ 库的简易方法,后续更多更复杂的操作还带进一步尝试。

参考链接 : 使用Java调取C++动态库。

你可能感兴趣的:(C++,Java,程序人生)