您的当前位置:首页正文

cmake系列-怎么在项目中使用C++动态库和静态库

来源:九壹网

在之前的文章中我们讲了怎么样生成C++动态库和静态库,那么生成之后怎么使用呢,这篇文章我们讨论一下这方面的内容。

这个问题主要有三种场景

  • 生成库文件的项目和使用库文件的项目属于同一个解决方案,意味着生成库文件的项目的所有内容我们也都能拿到。
  • 生成库文件的项目和使用库文件的项目属于不同的解决方案,而且我们只能拿到库的头文件和库文件。
  • 生成库文件的项目和使用库文件的项目属于不同的解决方案,并且我们能拿到生成库的项目的所有内容,或者能拿到库的头文件和库文件以及一些必要的配置文件。

接下来我们看具体示例,示例使用Linux系统,使用vscode编辑器,并且已经安装好了相关的工具链。

场景一

这个场景是生成库文件的项目和使用库文件的项目属于同一个解决方案。

为了模拟这种场景,我们可以写一个demo解决方案,这个解决方案中有一个生成动态库的项目demoA,一个生成静态库的项目demoB,以及一个生成可执行程序的项目demoC,demoC使用demoA和demoB生成的库。

来看一下demo结构

然后我们看一下具体的文件内容,这些内容里有一些在之前的文章中已经讲过了,这篇文章里就不赘述了。

先看一下顶层CMakeLists.txt

cmake_minimum_required(VERSION 3.25)

project(demo)

add_subdirectory(demoA)
add_subdirectory(demoB)
add_subdirectory(demoC)

这些指令之前的文章都讲过,这里不赘述了。

然后是demoA项目

// demoA.h

#ifndef DEMOA_H
#define DEMOA_H

class DemoA
{
public:
    DemoA() = default;

    ~DemoA() noexcept = default;

    void Func();
};

#endif
// demoA.cpp

#include "demoA.h"

#include <iostream>

void DemoA::Func()
{
    std::cout << "demoA" << std::endl;
}
project(demoA)

aux_source_directory(src srcfiles)

add_library(${PROJECT_NAME} SHARED ${srcfiles})

#include_directories(include)
target_include_directories(${PROJECT_NAME} PUBLIC include)

demoA项目的CMakeLists.txt中有个新指令target_include_directories(),我们讲一下这条指令的作用:

  • target_include_directories()和include_directories()的作用差不多,都是设置包含目录。
  • target_include_directories()可以指定设置具体目标的包含目录,比如这里的参数PROJECT_NAME,意思就是为目标demoA设置包含目录,而include_directories()会影响当前项目中所有目标。
  • target_include_directories()在设置包含目录时还可以指定该目录的访问属性,比如这里的PUBLIC,这个参数的作用就是在其他目标依赖这个目标时,就不用指定这个包含目录了,可以直接访问。
  • 参数include就是包含目录。
    可能比较抽象,后面我们还会讲到这个。

然后是demoB项目,demoB项目和demoA项目基本一样,就不赘述了

// demoB.h

#ifndef DEMOB_H
#define DEMOB_H

class DemoB
{
public:
    DemoB() = default;

    ~DemoB() noexcept = default;

    void Func();
};

#endif
// demoB.cpp

#include "demoB.h"

#include <iostream>

void DemoB::Func()
{
    std::cout << "demoB" << std::endl;
}
project(demoB)

aux_source_directory(src srcfiles)

add_library(${PROJECT_NAME} STATIC ${srcfiles})

#include_directories(include)
target_include_directories(${PROJECT_NAME} PUBLIC include)

接着我们看一下demoC项目,这个是这篇文章的重点内容,即怎么使用库

// main.cpp

#include "demoA.h"
#include "demoB.h"

int main()
{
    DemoA demoA;
    DemoB demoB;

    demoA.Func();
    demoB.Func();

    return 0;
}
project(demoC)

add_executable(${PROJECT_NAME} main.cpp)

#include_directories(../demoA/include)
#include_directories(../demoB/include)

target_link_libraries(${PROJECT_NAME} PRIVATE demoA demoB)

我们可以看到在demoC项目的CMakeLists.txt中有个新指令target_link_libraries(),这条指令就是在指定依赖的库,具体解释一下:

  • 参数PROJECT_NAME表示要为目标demoC设置依赖的库。
  • 参数PRIVATE和前面讲的指令target_include_directories()的参数PUBLIC差不多,也是设置访问属性,但是在这条指令里是个可选参数。
  • 参数demoA和demoB就是依赖项,写目标名就行,可以是动态库也可以是静态库。

需要注意的一点是,一般我们如果想要使用库,是需要指明库的位置的,但是这里因为是在同一个解决方案下,所以就不需要显式指定了,cmake隐式处理了。

场景二

这个场景是生成库文件的项目和使用库文件的项目属于不同的解决方案,而且我们只能拿到库的头文件和库文件。

为了模拟这种场景,我们创建一个新的解决方案,这个解决方案中只有一个用来生成可执行程序的项目demo,然后将场景一中demoA和demoB的头文件和库文件拿来用,新的项目demo使用拿到的这些头文件和库文件。

来看一下结构

这个解决方案中include文件夹里放的就是拿到的头文件,lib文件夹下放的就是拿到的库文件,main.cpp和上面的场景一里面是一样的,我们也来看一下

// main.cpp

#include "demoA.h"
#include "demoB.h"

int main()
{
    DemoA demoA;
    DemoB demoB;

    demoA.Func();
    demoB.Func();

    return 0;
}

重点在这个CMakeLists.txt文件里

cmake_minimum_required(VERSION 3.25)

project(demo)

include_directories(include)

link_directories(lib)

add_executable(${PROJECT_NAME} main.cpp)

target_link_libraries(${PROJECT_NAME} PRIVATE demoA demoB)

这里有个新的指令link_directories(),这个指令的作用就是设置要链接的库的位置,一般如果要用的库随便放了一个位置,都需要设置的,另外可以看到这里还使用了指令include_directories(),而在场景一里是不需要的,这个也是两种场景的区别。

场景三

这个场景是生成库文件的项目和使用库文件的项目属于不同的解决方案,并且我们能拿到生成库的项目的所有内容,或者能拿到库的头文件和库文件以及一些必要的配置文件。

这个场景的解决方法是通过编写一些cmake的配置文件以及使用指令find_package()来实现的,我们平时使用开源库其实就是这种场景,而且大多也都是采用这种解决方法,我们开发库的时候也推荐采用这种方法。

但是这种场景比较复杂,在这篇文章里就不展开了,之后会专门讨论这个内容的。

因篇幅问题不能全部显示,请点此查看更多更全内容

Top