• 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++ 动态库的调用。

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

    二.CLion

    1.inter.h

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

    1. // inter.h
    2. #include
    3. struct Point
    4. {
    5. float x, y, z;
    6. };
    7. Point add(Point p);

    2.test.cpp

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

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

    1. // test.cpp
    2. #include
    3. #include"inter.h"
    4. Point add(Point point) {
    5. point.x += 1;
    6. point.y += 1;
    7. point.z += 1;
    8. return point;
    9. }

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

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

    1. #ifdef __cplusplus
    2. extern "C" {
    3. #endif
    4. __declspec(dllexport) Point* Jna_add(Point point) {
    5. Point a = add(point);
    6. Point* ans = new Point[1];
    7. for (int i = 0; i < 1; ++i) {
    8. ans[i].x = a.x;
    9. ans[i].y = a.y;
    10. ans[i].z = a.z;
    11. }
    12. return ans;
    13. }
    14. #ifdef __cplusplus
    15. }
    16. #endif

    3.编译 .dylib / .so

    - .dylib For MacOs

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

    执行 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 标识动态库名称。

    Tips:

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

    4.可能遇到的坑

    A.__declspec attributes are not enabled

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

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

    修改后报错异常消失:

    B.ninja: no work to do.

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

    add_library(test SHARED test.cpp)
    

    添加后 Reload 配置即可。

    三.IDEA

    1.添加 jna 依赖

    1. <dependency>
    2. <groupId>net.java.dev.jnagroupId>
    3. <artifactId>jnaartifactId>
    4. <version>5.3.1version>
    5. dependency>

    该依赖主要用于 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 定义方法使用方式

    1. // java
    2. public interface JnaLibrary extends Library {
    3. JnaLibrary INSTANCE = Native.load("test", JnaLibrary.class); // 引入 C++ 动态库
    4. Point Jna_add(Point.ByValue point); // 定义使用方式
    5. @Structure.FieldOrder({"x", "y", "z"}) // 构建结构体
    6. class Point extends Structure {
    7. public Point() {}
    8. public static class UserValue extends Point implements Structure.ByValue {
    9. public UserValue(float x, float y, float z) {
    10. super(x, y, z);
    11. }
    12. }
    13. public Point(float x, float y, float z) {
    14. this.x = x;
    15. this.y = y;
    16. this.z = z;
    17. }
    18. public float x;
    19. public float y;
    20. public float z;
    21. }
    22. }

    第二部分为 main 主函数:

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

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

    Tips:

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

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

    四.总结

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

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

  • 相关阅读:
    学习Bootstrap 5的第十一天
    java基于springboot+vue的旅游博客旅游经验分享系统
    处理过程与工具
    大数据在电力行业的应用案例100讲(十七)-基于微服务架构的营配贯通设计
    毕业设计 基于大数据的服务器数据分析与可视化系统 -python 可视化 大数据
    配管信息管理工具之Apollo
    注册D8读卡器COM组件
    用照片预测人的年龄【图像回归】
    go语法入门2
    opencv python 深度学习垃圾图像分类系统 计算机竞赛
  • 原文地址:https://blog.csdn.net/BIT_666/article/details/128209396