• CMake Tutorial 巡礼(2)_添加库


    CMake Tutorial 巡礼(2)_ 添加库

    这是本系列的第三篇。
    上一篇,我们学习了CMake的一些基本操作,包括如何搭建编译系统、如何添加版本号及头文件、如何指定C++标准等等。
    我们在日常构建项目的过程中,用于项目组织的代码可能不止一个,这就引入了库的概念,本篇就来学习一下如何在CMake中添加库。

    本章导读

    在这里插入图片描述

    第二步:添加库

    Now we will add a library to our project. This library will contain our own implementation for computing the square root of a number. The executable can then use this library instead of the standard square root function provided by the compiler.

    现在我们将会在项目中添加一个库。这个库将包含我们自己对计算一个数的平方根的实现。可执行文件将可以调用这个库,而不是由编译器提供的标准平方根函数。

    For this tutorial we will put the library into a subdirectory called MathFunctions. This directory already contains a header file, MathFunctions.h, and a source file mysqrt.cxx. The source file has one function called mysqrt that provides similar functionality to the compiler’s sqrt function.

    在tutorial中我们将会把这个库放到一个名为MathFunctions的子路径下。这个路径已经包含有一个头文件,即MathFunctions.h,以及一个源文件mysqrt.cxx。这个源文件包含一个名为mysqrt的函数,提供了和编译器的sqrt函数类似的功能。

    小白按:原始的MathFunctions.hmysqrt.cxx文件内容如下所示:

    MathFunctions.h

    double mysqrt(double x);
    
    • 1

    mysqrt.cxx

    #include  
    
    // a hack square root calculation using simple operations 使用简单操作实现的开方运算函数
    double mysqrt(double x)
    {
      if (x <= 0) {
        return 0;
      }  
    
      double result = x;  
    
      // do ten iterations 做十次迭代
      for (int i = 0; i < 10; ++i) {
        if (result <= 0) {
          result = 0.1;
        }
        double delta = x - (result * result);
        result = result + 0.5 * delta / result;
        std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
      }
      return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    Add the following one line CMakeLists.txt file to the MathFunctions directory:

    添加以下行到MathFunctions路径下的CMakeLists.txt文件中:

    MathFunctions/CMakeLists.txt

    add_library(MathFunctions mysqrt.cxx)
    
    • 1

    To make use of the new library we will add an add_subdirectory() call in the top-level CMakeLists.txt file so that the library will get built. We add the new library to the executable, and add MathFunctions as an include directory so that the MathFunctions.h header file can be found. The last few lines of the top-level CMakeLists.txt file should now look like:

    为了使用新的库,我们将会在顶层的CMakeLists.txt文件中添加一个add_subdirectory()调用, 这样库就可以被编译。我们将新库添加到可执行中去,并且添加MathFunctions为一个包含目录,于是MathFunctions.h头文件就可以被找到。顶层路径的CMakeLists.txt文件的最后几行应该被改成如下形式:

    CMakeLists.txt

    # add the MathFunctions library 添加数学函数库
    add_subdirectory(MathFunctions)
    
    # add the executable 添加可执行文件
    add_executable(Tutorial tutorial.cxx)
    
    target_link_libraries(Tutorial PUBLIC MathFunctions)
    
    # add the binary tree to the search path for include files 为包含文件向搜索路径中添加二进制树
    # so that we will find TutorialConfig.h 这样我们才能找得到TutorialConfig.h头文件
    target_include_directories(Tutorial PUBLIC
                              "${PROJECT_BINARY_DIR}"
                              "${PROJECT_SOURCE_DIR}/MathFunctions"
                              )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Now let us make the MathFunctions library optional. While for the tutorial there really isn’t any need to do so, for larger projects this is a common occurrence. The first step is to add an option to the top-level CMakeLists.txt file.

    现在让我们把MathFunctions目录变成可选。尽管对于tutorial来说没有任何必要这样做,但对于大型项目来说这是一个常规操作。第一步就是在顶层的CMakeLists.txt文件中添加一个选项。

    CMakeLists.txt

    option(USE_MYMATH "Use tutorial provided math implementation" ON)
    
    # configure a header file to pass some of the CMake settings 指定一个头文件来向源代码中传递一些CMake设置
    # to the source code
    configure_file(TutorialConfig.h.in TutorialConfig.h)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    This option will be displayed in the cmake-gui and ccmake with a default value of ON that can be changed by the user. This setting will be stored in the cache so that the user does not need to set the value each time they run CMake on a build directory.

    这个选项将会显示在 cmake-gui以及 ccmake 中,以一个默认值为ON、可以由用户进行修改的形式出现。这个设置将会保存在缓存中,这样当用户在编译目录下运行CMake时,不需要每次都对它进行设置。

    The next change is to make building and linking the MathFunctions library conditional. To do this, we will create an if statement which checks the value of the option. Inside the if block, put the add_subdirectory() command from above with some additional list commands to store information needed to link to the library and add the subdirectory as an include directory in the Tutorial target. The end of the top-level CMakeLists.txt file will now look like the following:

    另一处修改是让编译和链接到MathFunctions库的操作变成有条件的。为了实现这个目标,我们将会创建一个检查这个选项的if声明。在if块之内,需要将上面提到的add_subdirectory()命令移动到此,并且追加一些添加信息的命令,这些命令需要链接到库,并且在Tutorial目标中添加一个子目录到包含目录中去。顶层的CMakeLists.txt文件末尾将会看起来如下所示:

    CMakeLists.txt

    if(USE_MYMATH)
      add_subdirectory(MathFunctions)
      list(APPEND EXTRA_LIBS MathFunctions)
      list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
    endif()
    
    # add the executable 添加可执行文件
    add_executable(Tutorial tutorial.cxx)
    
    target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
    
    # add the binary tree to the search path for include files 为包含文件向搜索路径中添加二进制树
    # so that we will find TutorialConfig.h 这样我们才能找得到TutorialConfig.h头文件。
    target_include_directories(Tutorial PUBLIC
                               "${PROJECT_BINARY_DIR}"
                               ${EXTRA_INCLUDES}
                               )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Note the use of the variable EXTRA_LIBS to collect up any optional libraries to later be linked into the executable. The variable EXTRA_INCLUDES is used similarly for optional header files. This is a classic approach when dealing with many optional components, we will cover the modern approach in the next step.

    请注意,使用变量EXTRA_LIBS收集任何的可选库,以便稍后链接到可执行文件中。变量EXTRA_INCLUDES类似地用于可选头文件。这是处理许多可选组件时的经典方法,我们将在下一步中介绍现代方法。

    The corresponding changes to the source code are fairly straightforward. First, in tutorial.cxx, include the MathFunctions.h header if we need it:

    对源代码的相应修改相当直接。首先,在tutorial.cxx中,如果需要的话,请包含MathFunction.h头文件:

    tutorial.cxx

    #ifdef USE_MYMATH
    #  include "MathFunctions.h"
    #endif
    
    • 1
    • 2
    • 3

    接着,在同一个文件里,设置USE_MYMATH控制什么情况下会使用什么版本的平方根函数:

    tutorial.cxx

    #ifdef USE_MYMATH
      const double outputValue = mysqrt(inputValue);
    #else
      const double outputValue = sqrt(inputValue);
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Since the source code now requires USE_MYMATH we can add it to TutorialConfig.h.in with the following line:

    由于源代码现在需要USE_MYMATH,我们可以使用以下行将其添加到TutorialConfig.h.in文件中去:

    TutorialConfig.h.in

    #cmakedefine USE_MYMATH
    
    • 1

    小白按:最终改造后,各个文件的内容分别是:
    CMakeLists.txt

    cmake_minimum_required(VERSION 3.10)
    
    # set the project name and version
    project(Tutorial VERSION 1.0)
    
    # specify the C++ standard
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    
    option(USE_MYMATH "Use tutorial provided math implementation" ON)
    
    # configure a header file to pass some of the CMake settings
    # to the source code
    configure_file(TutorialConfig.h.in TutorialConfig.h)
    
    if (USE_MYMATH)
        #add the MathFunctions library
        add_subdirectory(MathFunctions)
        list(APPEND EXTRA_LIBS MathFunctions)
        list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
    endif()
    
    # add the executable
    add_executable(Tutorial tutorial.cxx)
    
    target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
    
    # add the binary tree to the search path for include files
    # so that we will find TutorialConfig.h
    target_include_directories(Tutorial PUBLIC
                               "${PROJECT_BINARY_DIR}"
                               ${EXTRA_INCLUDES}
                               )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    tutorial.cxx

    // A simple program that computes the square root of a number
    #include 
    #include 
    #include   
    
    #include "TutorialConfig.h"
    #ifdef USE_MYMATH
    #    include "MathFunctions.h"
    #endif
     
    int main(int argc, char* argv[])
    {
      if (argc < 2) {
        // report version
        std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
                  << Tutorial_VERSION_MINOR << std::endl;
        std::cout << "Usage: " << argv[0] << " number" << std::endl;
        return 1;
      }
      
      // convert input to double
      const double inputValue = std::stod(argv[1]);
     
      // calculate square root
    #ifdef USE_MYMATH
      const double outputValue = mysqrt(inputValue);
    #else
      const double outputValue = sqrt(inputValue);
    #endif
      std::cout << "The square root of " << inputValue << " is " << outputValue
                << std::endl;
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    TutorialConfig.h.in

    // the configured options and settings for Tutorial
    #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
    #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
    #cmakedefine USE_MYMATH
    
    • 1
    • 2
    • 3
    • 4

    Exercise: Why is it important that we configure TutorialConfig.h.in after the option for USE_MYMATH? What would happen if we inverted the two?

    【练习】为什么在USE_MYMATH选项之后再配置TutorialConfig.h.in是很重要的?如果把这二者的顺序对调,会发生什么?

    小白按:因为TutorialConfg.h.in中使用了USE_MYMATH变量,如果对调二者顺序,则会出现未定义错误。

    Run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool. Then run the built Tutorial executable.

    运行 cmake可执行文件或 cmake-gui 来指定项目,并且用你选定的编译工具来进行编译。然后运行Tutorial的可执行文件。

    Now let’s update the value of USE_MYMATH. The easiest way is to use the cmake-gui or ccmake if you’re in the terminal. Or, alternatively, if you want to change the option from the command-line, try:

    现在让我们来更新USE_MYMATH的值,最简单的办法是使用 cmake-gui ,或者如果你使用终端环境的话可以直接使用cmake。也或者,如果你想要在命令行中修改这个选项,尝试:

    cmake ../Step2 -DUSE_MYMATH=OFF
    
    • 1

    Rebuild and run the tutorial again.

    重新编译并运行tutorial.exe。

    Which function gives better results, sqrt or mysqrt?

    哪种函数给出了更好的结果,sqrt还是mysqrt

    小白按:与上一篇中采用相同的测试数,得到的结果为:

    Computing sqrt of 4.29497e+09 to be 2.14748e+09
    Computing sqrt of 4.29497e+09 to be 1.07374e+09
    Computing sqrt of 4.29497e+09 to be 5.36871e+08
    Computing sqrt of 4.29497e+09 to be 2.68435e+08
    Computing sqrt of 4.29497e+09 to be 1.34218e+08
    Computing sqrt of 4.29497e+09 to be 6.71089e+07
    Computing sqrt of 4.29497e+09 to be 3.35545e+07
    Computing sqrt of 4.29497e+09 to be 1.67773e+07
    Computing sqrt of 4.29497e+09 to be 8.38878e+06
    Computing sqrt of 4.29497e+09 to be 4.19465e+06
    The square root of 4.29497e+09 is 4.19465e+06
    Computing sqrt of 10 to be 5.5
    Computing sqrt of 10 to be 3.65909
    Computing sqrt of 10 to be 3.19601
    Computing sqrt of 10 to be 3.16246
    Computing sqrt of 10 to be 3.16228
    Computing sqrt of 10 to be 3.16228
    Computing sqrt of 10 to be 3.16228
    Computing sqrt of 10 to be 3.16228
    Computing sqrt of 10 to be 3.16228
    Computing sqrt of 10 to be 3.16228
    The square root of 10 is 3.16228
    Tutorial Version 1.0
    Usage: Tutorial number
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    显然,sqrt得到了更高精度的结果,对于大数的开方运算,甚至得到了错误的答案,这是因为mysqrt中使用的方法只迭代了10次,远远达不到需要的精度。

    【水平所限,错漏难免,创作不易,轻喷勿骂】

    在这里插入图片描述

  • 相关阅读:
    厚膜功率电阻器制造:优化性能
    消息队列之kafka
    通用代码生成器应用场景四,跨编程语言翻译
    哪个电脑录屏软件好用又免费?十大好用的免费录屏软件排行
    新能源汽车为什么越来越受欢迎?它的未来发展前景究竟如何?火象 2022-06-28 10:07
    数据匿名化的几种技术手段
    虚拟机网络配置
    vue3中的reactive赋值问题
    docker搭建kafka集群
    【无标题】
  • 原文地址:https://blog.csdn.net/horsee/article/details/126775403