CMake动态库

前言

相比较于静态库,动态库需要的是得到一些运行时的量,这是和静态库本质的区别

实现动态库

  • 确认平台
    • 我们配置动态库要注意是在什么平台下,如果是在 win 下需要有静态库作为帮手,把动态库导入
    • Windows 和 Linux 在处理静态库和动态库(也称为共享库)时有一些差异,这主要归因于它们的链接和加载机制的不同。这些差异影响了在各自平台上构建和运行程序的方式。

Windows 平台:

  1. 静态库(. Lib):

    • 在 Windows 上,静态库通常以 .lib 文件形式出现。
    • 当程序编译时,静态库的内容被复制到最终的可执行文件(.exe)中。
    • 因此,部署时不需要静态库文件,因为所有必需的代码都包含在 .exe 文件内。
  2. 动态链接库(DLL,. Dll):

    • 动态链接库(Dynamic Link Libraries, DLLs)是 Windows 上的共享库。
    • 程序在运行时动态地链接到这些库,而不是在编译时。
    • 这意味着,为了运行程序,必须有相应的 .dll 文件在可访问的路径上,例如程序所在目录或系统目录。

在 Windows 上,有时即使使用动态库,也需要一个小型的静态库(通常称为导入库),它包含了定位和链接到动态库的指令。

Linux 平台:

  1. 静态库(. A):

    • Linux 上的静态库通常以 .a 文件形式存在。
    • 与 Windows 类似,静态库的内容在编译时被包含到最终的可执行文件中。
    • 运行程序时不需要静态库文件。
  2. 共享库(. So):

    • Linux 上的共享库通常是 .so(shared object)文件。
    • Linux 上的动态链接处理比 Windows 更灵活。程序可以直接链接到共享库,不需要额外的静态导入库。
    • 运行时需要能够访问这些共享库,但不需要编译时的静态库。

总结:

  • Windows 平台上,静态库(. Lib)通常是必需的,即使是对于动态链接的情况(通过导入库)。而动态库(. Dll)在运行时必须可用。
  • Linux 平台上,静态库(. A)用于完全静态链接的情况,共享库(. So)用于动态链接。动态链接在 Linux 上不依赖于编译时的静态库。

这些差异反映了不同操作系统平台的底层架构和设计哲学。

  • Win 下如何导入动态库
    • 当我们需要 dll 时,在 .cpp 文件的同级 cmakelists 下添加 SHARED 如下
    • [Pasted image 20231223100002.png]
    • 就可以生成 dll 文件
    • [YXOPng]
    • 但是问题在于,构建 exe 的时候会发生损坏
    • [ZKN 6OL~XG 9 B 1. Png]
    • 原因上面讲过,所以在 win 平台下我们需要加上 __declspec(dllexport).cpp· 文件以生成静态库帮助导入动态库
    • [Pasted image 20231223094956.png]
    • 如果没有加上就会出现文件损毁
    • [ZKN ng]
    • 当我们加上后,在 cmakelists 中添加静态位置,再次进行构建就可以出现 exe 可执行文件 [Pasted image 20231223095724.png]

Win 下如何优雅地导入动态库

  1. 为了优雅地引入 __declspec(dllexport),需要去通过宏定义的方式去实现 #define CMAKE_STUDY_API __declspec(dllimport) 这样我们就可以通添加 CMAKE_STUDY_API 来使得程序更加易读
  2. 如果在编译 DLL 的时候定义了,那么会被定义为,这意味着随后所有标记了这个宏的类和函数都是要被导出的。EXPORT``EXPORT``CMAKE_STUDY_API``__declspec(dllexport)
  3. 如果没有定义,意味着当前你可能在编译使用 DLL 的应用程序而非 DLL 本身。此时会被定义为,标记的类和函数表示从 DLL 中导入。EXPORT``CMAKE_STUDY_API``__declspec(dllimport)

例如,在 DLL 的项目中你可能会这样写:

1
2
3
4
5
6
7
8
// Define EXPORT when compiling the DLL
#define EXPORT // This is typically done in the project settings not in the code directly

#include "your_header.h" // The header file containing the CMAKE_STUDY_API macro

CMAKE_STUDY_API void SomeFunction() {
// Function implementation that is to be exported from the DLL
}

然后,在使用这个 DLL 的其他项目中,你不定义,则会变成用于导入的宏:EXPORT``CMAKE_STUDY_API

1
2
3
4
5
6
7
// No EXPORT defined here, as we're using the DLL

#include "your_header.h" // The header file containing the CMAKE_STUDY_API macro

void UseSomeFunction() {
SomeFunction(); // Call the function imported from the DLL
}

Win 下如何表示导出导入 dll

在 Windows 操作系统中,有两种特殊的程序文件叫做 DLL(动态链接库)文件:

  1. 导出(Export):想象一个工厂,它生产各种零件。这个工厂希望其他工厂或商店能够买到它的零件,所以它需要告诉大家:“这些是我制作的零件,欢迎购买!” 在程序中,如果你制作了一个 DLL,并且你希望其他程序能使用 DLL 里面的功能(函数、变量等),你需要“导出”这些功能。

  2. 导入(Import):现在想象你运营着另外一个工厂,你需要购买之前那个工厂的零件来制造你的产品。那么你就需要“导入”那些零件。在程序中,如果你的程序想要使用其他 DLL 文件里的功能,你就需要“导入”这些功能。

在 C++中,为了告诉电脑哪些功能是要被导出的,哪些是要被导入的,我们需要使用一些特殊的关键字。在 Windows 中,我们用 __declspec(dllexport) 来标识要导出的功能,用 __declspec(dllimport) 来标识要导入的功能。

所以当我们结合上面通过宏的=来优化,我们可以写下 export.h

1
2
3
4
5
6
7
8
9
10
11
#pragma once

#ifdef EXPORT

#define CMAKE_STUDY_API __declspec(dllexport)

#else

#define CMAKE_STUDY_API __declspec(dllimport)

#endif

代码段:

  • 当在制作 DLL 文件时,你会在你的代码中定义一个叫 EXPORT 的宏。这个宏只在制作 DLL 文件时使用。
  • CMAKE_STUDY_API 这个宏将会根据是否定义了 EXPORT 来改变它的行为:
    • 如果定义了 EXPORT(意味着你正在制作 DLL),CMAKE_STUDY_API 就代表 __declspec(dllexport),标识功能为“出售”状态。
    • 如果没有定义 EXPORT(意味着你在使用别人的 DLL),CMAKE_STUDY_API 就代表 __declspec(dllimport),标识功能为“购买”状态。

所以,CMAKE_STUDY_API 是一个切换开关,它可以根据你是正在制作还是使用 DLL,来切换为相应的状态。以上都是作为开发者需要知道的,而用户无需知道的,导入导出的作用就是为了运行的时候 dll 可以连接找到我们的函数。

经过以上,我们就可以放把 dll 到 bin 当中,以及头文件, 以提供给用户:
[B%X7EMLUX0`OHH{X1SB_U8S 1.png]
[Pasted image 20231223102728.png]
如此也可以生成 exe 文件用于执行

另外需要注意的是,我们生成的 .exe 文件需要去加上 .dll 文件,否则会找不到文件错误
曾经在游玩游戏的时候是否有出现过类似如下,找不到 .dll
[Pasted image 20231223095407.png]
事实上需要保证 .dll.exe 同级目录下或者在环境变量下就可以运行

注意事项 :

由于在 win 系统下我们的动态库也是出于不确定的情况,所以需要依赖 lib,而不只是 bin,所以你需要在 .cpp 类文件加入 __declspec(dllexport) [Pasted image 20231223022548.png]
同时还有 cmake 文件要加上 shared
[Pasted image 20231223022659.png]