在进行C++时,选择什么样的编译器显得非常的重要。与其它的语言不同,C++的程序最终由头文件(.h)以及库文件(.dll或.so)组件。
不同的应用(库)协助工作时,同样需要对文件及库文件的支持,而不同的平台上的库文件是不一致的,所以在跨平台的开发中,我们更喜欢使用cmake来进行编译链接。

所以cmake并不是包管理工具,他并不能够解决包的依赖问题,而仅仅解决的跨平台的问题。

编译器

将源代码变成可执行的应用需要一个叫做编译器的东西,如果在应用中使用了已经成型的第三方库,或是自己的应用较大,需要多个模块配合的话。还需要将这些模拟连接起来,最终形成一个可以使用的应用程序。

就这像在一个标准的Spring boot项目在构建时,能够构建出一个包含自己和其它第三方库,可被直接运行的xxx.jar和一个只包含自己的可被用做第三方库的xxx.jar.original文件。

而将源代码变成可执行的字节码的过程叫做编译,将源代码自己生成的库与其它库连在一起,最后形成可执行应用程序的过程则叫做链接。

完成上述功能的工具则称为编译器。

在java开发中,我们需要针对不同的操作系统下载不同的jdk。这个jdk中有一部分的作用便是java的编译器。在C++开发中,macos中已经默认安装了一款名为clang的编译器,所以我们并不需要单独的安装它。

需要叫各个系统中c++的编译器不同,比如macos中默认安装的是clang。除此以外,编译器还有注明的GNU(gcc、g++)。而不同的编译器均拥有自己的特点,我们在开发应用时,也会将这些编译器的特点加入到我们的源代码中。然后编译器在根据源代码来编译为字节文件。在有些特性只有特定的编译器才支持,这也是为什么有的应用只能使用特定编译器编译才能工作的原因。

依赖管理

java中有比较著名的mavengradle包管理器,它能够自动的为我们下载依赖,接着调用指定的jdk进行编译,能够完成java应用的打包工作。

不太幸运的是c++应用中并没有这么强大的包管理工具。但一个多模块有依赖的项目,实现的思想仍然是相同的。

如同npm下管理的包在package.json中声明了需要的包及版本号,然后使用npm install的命令来安装。C++的应用则可以在CMakeLists.txt来声明版本号。然后使用其它的方式来安装。

加载依赖

前面讲了cmake是一款跨平台的编译器,而它对应的配置文件则是CMakeLists.txt,在该文件中说明了对其它第三方包的依赖。

find_package()

CMakeLists.txt声明依赖的方式大体分为两种,第一种是使用find_package
比如:find_package(JsonCpp REQUIRED)。此时CMake会先查找其内部包,如果没有在内部包中找到,则会在当前目录下查找文件FindJsonCpp.cmake文件。如果找到了FindJsonCpp.cmake文件,则会按该文件的设置加载包;如果没有找到,由于第二个参数设置的值为REQUIRED,则会抛出一个异常。

所以如果某些第三方包提供了FindXxxx.cmake文件的话,可以轻松的使用find_package(name, REQUIRED)函数来加载第三方包。

一般情况下,如成功的找到第三方包将会定义如下变量:

# 找到包时,该值为true
Xxxx_FOUND
# 该包头文件所在位置
Xxxx_INCLUDE_DIRS 或 Xxxx_INCLUDES
# 该包库文件所在位置
Xxxx_LIBRARY 或 Xxxx_LIBRARIES 或 Xxxx_LIBS
# 可以使用message(${Xxxx_LIBRARIES})来打印变量的值

pkg_check_modules()

第二种方式是借助于PkgConfig

find_package(PkgConfig REQUIRED)

当系统未安装PkgConfig则会提到一个未找到PkgConfig的错误,此时则需要在系统中安装PkgConfig

有了PkgConfig后便可以使用其提供的pkg_check_modules包检测方法来获取第三方包的信息了:

pkg_check_modules(JSONCPP jsoncpp)

其中JSONCPP为别名可以随便起,jsoncpp为模块名。

此时当存在jsoncpp时,则系统变量JSONCPP__FOUND的值为true,否则为false。如果当前项目必须依赖于某个第三方包,则可以加入REQUIRED,比如:pkg_check_modules(XXX xxx REQUIRED)此时当不存在xxx时将会发生一个异常。

加载

无论是通过find_package()还是通过pkg_check_modules来获取第三方依赖包,其本质上都是在:检查是否存在某个包,如果存在则返回该包的信息,如果不存在,则将系统变量Xxxx_FOUND设置为false。除此以外,并没有其它作用。

也就是说上述两个方法并没有加载对其第三方的依赖。这所以要使用命令来查找某个包的信息(包的库文件、头文件位置),也是考虑了跨平台的原因。在跨平台的情况下,我们不能使用诸如windows中的c:\xxx\xxx或xunix中的~/xxx/xxx任一形式。所以在加载某个依赖前,需要通过上述方法来动态获取包的信息,如果当前使用的是windows系统,则可以是c:\xxx\xx的形式,如果使用的是xunix系统,则可能是/usr/local/xxx的形式。

加载第三方库需要将头文件使在位置使用include_directories包含进来,并将库使用target_link_libraries链接进来。

include_directories(project-name ${Xxxxx_INCLUDE_DIRS})
target_link_libraries(project-name ${Xxxxx_LIBRARIES})

包管理器

Microsoft提供了vcpkg来安装c++应用的依赖。使用shell安装方式如下:

# clone仓库
git clone https://github.com/Microsoft/vcpkg.git --depth=1
# 进入仓库
cd vcpkg
# 运行安装脚本
./bootstrap-vcpkg.sh
# 设置为全局安装,以后再安装其它包的时候,都是全局的
./vcpkg integrate install

执行完全局安装后,将会得到一个提示:

panjie@panjies-iMac vcpkg % ./vcpkg integrate install
Applied user-wide integration for this vcpkg root.

CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=/Users/panjie/github/Microsoft/vcpkg/scripts/buildsystems/vcpkg.cmake"

它在说如果我们再使用CMake工具的话,则需要在CMake命令后追加上述参数。如果我们使用了Clion编辑器,则需要将其添加到CMake options中:

此时Clion中的Cmake则会在执行时自动加入上述参数。自此以后Clion便可以和cmake及vcpkg包管理器愉快的工作在一起了。

安装特定的包

比如我们安装一个用于json序列化的jsoncpp,则可以在vcpkg中执行:./vcpkg install jsoncpp

panjie@panjies-iMac vcpkg % ./vcpkg install jsoncpp
Computing installation plan...
...
The package jsoncpp:x64-osx provides CMake targets:

    find_package(jsoncpp CONFIG REQUIRED)
    target_link_libraries(main PRIVATE jsoncpp_object jsoncpp_static)

最后按照提示将上述代码添加到自己项目中的CMakeLists.txt中,然后便可以愉快的在项目中使用第三方jsoncpp包了:

    add_executable(yz-main yz-main.cpp)
    find_package(jsoncpp CONFIG REQUIRED)
    target_link_libraries(yz-main PRIVATE jsoncpp_object jsoncpp_static)    

此后便可以在当前工程中自由的使用jsoncpp来完成json的序列化与反序列化工作了。

总结

最后使用java与c++项目做对比总结:

  1. 两者都属于编译型语言,都需要先编译。
  2. java源文件使用jdk来编译,不同的操作系统需要安装不同的JDK安装包;C++需要使用C++编译器进行编译,不同的操作系统需要下载不同的编译器。
  3. java的编译器有多个版本,比如oracle自家的jdk,或是第三方的openjdk;C++的编译器也有多个版本,比如clang、GNU。在实际的开发过程中,需要按自己的需求进行下载、安装配置。
  4. java中有统一的包管理、安装工具mavengradle;C++有统一的包管理工具vcpkg,有统一的安装工具cmake
  5. vcpkg想与cmake愉快的工作,需要配置一些参数。该参数不同的计算机会有不同,需要按安装vcpkg情况进行配置。
03-05 21:01