C++最佳實踐之編譯篇

C++最佳實踐之工程編譯

在大型c/c++工程開發中,往往會涉及多級CMakeLists.txt的調用,并且調用方式錯綜復雜,主要有以下兩種方式:

1. 子目錄中的CMakeList.txt獨立生成目標,不作為主目標生成過程的依賴關系(比如dev_tool、driver、ut_test等),與主目標并無任何關系。2. 子目錄中的CMakeList.txt作為主目標的依賴源文件,不單獨生成目標,作為主目標生成過程主的部分源文件,通常以生成.a源文件的方式提供給主CMakeList.txt使用。

一、工程目錄結構

下面給出了測試工程目錄,進行了兩項測試:

  • unit—test目錄作為獨立生成目標,其CMakeLists.txt在主CMakeList.txt主被調用;

  • subfunc和subsubfunc作為主CMakeList.txt向下兩級的依賴,為主CMakeList.txt提供源文件支持,其CMakeLists.txt為逐級調用的方式:
    CMakeList.txt->subfunc/CMakeList.txt->subfunc/subfuncfunc/CMakeLists.txt

具體目錄結構如下:

在這里插入圖片描述

  • build: 編譯目錄,生成的目標執行文件、靜態庫、中間緩存文件都在此處
  • inc:主頭文件目錄
  • src:主源文件目錄
  • subfunc:依賴的一級子目錄
  • uinit-test:單元測試目錄,獨立生成的目標文件
  • CMakeList.txt:最上層,主CMakeList.txt

二、工程源代碼

2.1、工程源代碼
cmake_minimum_required(VERSION 3.8)       # cmake 版本
PROJECT(cmaketest)                        # 工程名#set project name
set(PROJECT_NAME cmaketest)               # 設置工程名字set(CMAKE_CXX_STANDARD 17)                # 設置C++標準為C++17
set(CMAKE_CXX_STANDARD_REQUIRED ON)       # 設置本地頭文件路徑,注意:子目錄的頭文件是通過target_include_directories添加到${PROJECT_NAME}中
INCLUDE_DIRECTORIES(inc                                   #上層頭文件${SUB_INCLUDE_DIR}                    #下級頭文件
)#將源文件路徑添加到變量src_list中
AUX_SOURCE_DIRECTORY(.          SRC_LIST)
AUX_SOURCE_DIRECTORY(src        SRC_LIST)# 7.生成目標(可執行文件):cmaketest
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_LIST})# 8.設置編譯時依賴的subfunc靜態庫
target_link_libraries(${PROJECT_NAME}    #目標:tcusubfunc        # sub子目錄下的靜態庫文件subsubfunc     # subsub子目錄下的靜態庫文件
)# 9.添加子目錄,這樣子目錄中的CMakeLists.txt才會被調用# 調用subfunc子目錄中的CMakeLists.txt,生成靜態庫而不生成新目標,目標與主CMakeLists.txt中設定的一致
add_subdirectory(subfunc) 
# 調用unit-test子目錄中的CMakeLists.txt,生成新目標,目標與主CMakeLists.txt中設定的無關,僅僅是調用
add_subdirectory(unit-test)

注意

include_directories包含的頭文件路徑可以被各級子目錄中的目標所引用;target_include_directories包含的頭文件只能被特定目標使用;采用變量傳遞的方式(${sub_include_dir}引入子路徑)引入子目錄的頭文件路徑

cmakettest/main.cpp:

#include <iostream>
#include <string>
#include "func1.hpp"   //應用層頭文件1
#include "func2.hpp"   //應用層頭文件2int main(int argc, char *argv[])
{func1();          //調用上層func1func2();          //調用上層func2return 0;
}

cmakettest/inc/func1.hpp:

#ifndef __FUNC1_HPP__
#define __FUNC1_HPP__int func1(void);#endif

cmakettest/inc/func2.hpp:

#ifndef __FUNC2_HPP__
#define __FUNC2_HPP__int func2(void);#endif

cmakettest/src/func1.cpp:

#include "subfunc.hpp"   //subfunc頭文件
#include "func1.hpp"     //應用層頭文件1
#include <iostream>
#include <string>int func1(void)
{std::cout<<"------------func1函數調用開始----------"<<std::endl;subfunc1();std::cout<<"------------func1函數調用結束----------"<<std::endl<<std::endl;return 0;
}

cmakettest/src/func2.cpp:

#include "subfunc.hpp"   //subfunc頭文件
#include "func2.hpp"     //應用層頭文件1
#include <iostream>
#include <string>int func2(void)
{std::cout<<"------------func2函數調用開始----------"<<std::endl;subfunc2();std::cout<<"------------func2函數調用結束----------"<<std::endl;return 0;
}
2.2、subfunc以及subsubfunc子目錄

cmaketest/subfunc/CMakeLists.txt

# 1.將本目錄下的所有.c 文件添加到SUB_DIR_LIB_SRCS變量
AUX_SOURCE_DIRECTORY(. SUB_DIR_SRC_LIST)# 2.設置當前的頭文件路徑
set(SUB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}          # 當前源文件路徑${SUB_SUB_INCLUDE_DIR}               # 由下層subsubfunc目錄傳遞的頭文件路徑CACHE INTERNAL "subfunc include dir" # 這個字符串相當于對變量SUB_INCLUDE_DIR的描述說明,不能省略,但可以自己隨便定義,只有添加了這個描述SUB_INCLUDE_DIR變量才能被上層CMakeLists.txt調用!!!
)MESSAGE(STATUS "subfunc層頭文件路徑 :${SUB_INCLUDE_DIR}")# 3.生成靜態庫
add_library(subfunc ${SUB_DIR_SRC_LIST})# 4.添加subsubfunc子目錄,這樣子目錄中的CMakeLists.txt才會被調用
add_subdirectory(subsubfunc)

cmaketest/subfunc/subfunc.hpp:

#ifndef __SUB_FUNC_HPP__
#define __SUB_FUNC_HPP__int subfunc1(void);
int subfunc2(void);#endif

cmaketest/subfunc/subfunc.cpp:

#include "subfunc.hpp"
#include "subsubfunc.hpp"
#include <iostream>
#include <string>int subfunc1(void)
{std::cout<<"------subfunc1函數調用開始------"<<std::endl;/* 中間調用subsubfunc1函數 */
subsubfunc1();std::cout<<"------subfunc1函數調用結束------"<<std::endl;return 0;
}
int subfunc2(void)
{std::cout<<"------subfunc2函數調用開始------"<<std::endl;
subsubfunc2();/* 中間調用subsubfunc2函數 */std::cout<<"------subfunc2函數調用結束------"<<std::endl;return 0;
}

cmaketest/subfunc/subsubfunc/CMakeLists.txt

# 1.將本目錄下的所有.c 文件添加到SUB_DIR_LIB_SRCS變量
AUX_SOURCE_DIRECTORY(. SUB_SUB_DIR_SRC_LIST)# 2.設置當前的頭文件路徑
set(SUB_SUB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}              # 當前源文件路徑CACHE INTERNAL "subsubfunc include dir"  # 這個字符串相當于對變量SUB_SUB_INCLUDE_DIR的描述說明,不能省略,但可以自己隨便定義,只有添加了這個描述SUB_SUB_INCLUDE_DIR變量才能被上層CMakeLists.txt調用!!!
)MESSAGE(STATUS "subsubfunc層頭文件路徑 :${SUB_SUB_INCLUDE_DIR}")# 3.生成靜態庫
add_library(subsubfunc ${SUB_SUB_DIR_SRC_LIST})

分析

1. 頭文件目錄變量的逐級向上傳遞,通過設置sub_sub_include_dir變量(必須包含cache internel “subsubfunc include dir”描述),將subsubfunc下的頭文件路徑傳遞給了上級subfunc的CMakeList.txt;2. 通過設置SUB_INCLUDE_DIR變量(必須添加CACHE INTERNAL "subfunc include dir"描述),將subfunc文件下的頭文件路徑(包含之前獲得的${SUB_SUB_INCLUDE_DIR})傳遞給了最上層文件下的CMakeLists.txt。
2.3、UT子目錄

cmaketest/unit-test/CMakeLists.txt

add_executable(unit-test unit-test.cpp)
target_link_libraries(unit-test boost_system pthread)

cmaketest/unit-test/unit-test.cpp

#include <boost/asio.hpp> 
#include <iostream>int main(int argc,char* argv[])
{std::cout<<"unit-test代碼調用!!!"<<std::endl;return 0;
}

三、編譯與實踐

在項目路徑下創建/build文件夾,用于存放cmake編譯過程中生成的各種中間文件、庫、最終目標文件等:

cd build
cmake ..
make

在cmake編譯的過程中發現了一個問題,就是一開始執行cmake . .時候,并沒有完成頭文件變量的傳遞,導致make編譯出錯。執行結果如下:

在這里插入圖片描述

make,結果如下:

在這里插入圖片描述

可以看到,由于SUB_SUB_INCLUDE_DIR變量并沒有傳遞到subfunc層的CMakeLists.txt,從而導致了在subfunc層并沒有包含全部發頭文件路徑,導致編譯的時候找不到subsubfunc.hpp。

解決方案:

經過反復測試發現,多執行幾次cmake…(三次以上)就可以解決這個問題,猜測是因為多次執行cmake的過程完成了SUB_SUB_INCLUDE_DIR和SUB_INCLUDE_DIR變量向上層的傳遞:
在這里插入圖片描述

四、build目錄分析

在這里插入圖片描述

  • unit-test作為獨立的子目錄,生成了獨立的目標文件unit-test;
  • subfunc下生成了libsubfunc.a的靜態庫文件,在subsubfunc下生成了libsubsubfunc.a的靜態庫文件,這兩個庫文件供最上層CMakeLists.txt調用,并最終生成一個目標文件cmaketest;

五、程序執行結果

在這里插入圖片描述

六、總結

1. 一種是獨立的unit-test生成獨立的目標文件,與主CMakeLists.txt僅有一個調用與被調用的關系,并不存在任何的編譯依關系;2. 另一種多級CMakeLists.txt調用之間存在上下級的依賴關系,下層的源代碼給上層的調用提供支持(以生成靜態庫的方式),這里進行了分層設計。3. 下層的頭文件路徑僅傳遞給調用的上一層而不會傳遞給最上層,這種變量傳遞的方式提高了代碼的分層性,這里需要注意變量的設置(CACHE INTERNAL屬性的必須添加)以完成下層到上層的變量傳遞,上述的兩種分層CMakeLists.txt調用方式可覆蓋基本的全部開發場景。

七、未完待續

下章將繼續介紹C++相關的工程能力。歡迎關注知乎:北京不北歡迎關注douyin:near.X (北京不北)歡迎+V:beijing_bubei獲得免費答疑,長期技術交流。

八、參考文獻

,這里進行了分層設計。

3. 下層的頭文件路徑僅傳遞給調用的上一層而不會傳遞給最上層,這種變量傳遞的方式提高了代碼的分層性,這里需要注意變量的設置(CACHE INTERNAL屬性的必須添加)以完成下層到上層的變量傳遞,上述的兩種分層CMakeLists.txt調用方式可覆蓋基本的全部開發場景。

七、未完待續

下章將繼續介紹C++相關的工程能力。歡迎關注知乎:北京不北歡迎關注douyin:near.X (北京不北)歡迎+V:beijing_bubei獲得免費答疑,長期技術交流。

八、參考文獻

https://blog.csdn.net/weixin_42700740/article/details/126364574

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/697755.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/697755.shtml
英文地址,請注明出處:http://en.pswp.cn/news/697755.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

virtualenv env_name 使用 virtualenv 創建 python 虛擬環境

為什么要用這個 win7 32 環境下 pycharm 只能用低版本的&#xff0c;比如 2016,2018 此時pycharm 圖形界面創建的 虛擬環境版本很低&#xff0c;有些包不兼容&#xff0c;因此用 virtualenv 模塊&#xff0c;可以創建 20 版本以上的虛擬環境 virtualenv env_name官方文檔 http…

如何選擇最適合的圖紙加密軟件?安秉網盾軟件用戶體驗及性價比

安秉網盾圖紙加密軟件是一款功能強大的圖紙加密工具&#xff0c;具有以下特點和優勢&#xff1a; 全盤加密&#xff1a;安秉網盾采用先進的加密算法&#xff0c;能對文件、文件夾、磁盤等數據進行全面加密&#xff0c;確保數據在存儲和傳輸過程中的安全性。 監控與審計&#x…

Swift基礎知識:26.Swift類型轉換

在 Swift 中&#xff0c;類型轉換是指將一種類型的實例轉換為另一種類型的操作。Swift 提供了三種類型轉換操作&#xff1a;as?、as! 和 is。 as? 和 as! as?&#xff1a;可選類型轉換。如果轉換成功&#xff0c;返回一個可選類型&#xff0c;值為轉換后的類型&#xff1b…

編程筆記 Golang基礎 024 映射

編程筆記 Golang基礎 024 映射 一、映射二、映射的定義與初始化三、基本操作四、綜合示例程序 Go語言中的映射&#xff08;map&#xff09;是一種關聯數組或哈希表數據結構&#xff0c;它存儲鍵值對&#xff0c;其中每個鍵都是唯一的。在Go中&#xff0c;你可以使用 map[keyTy…

關于el-select值的回顯問題 : 框內顯示label值還是value值

<el-form-item label"狀態" prop""><el-selectv-model"roleForm.state"class"m-2"size"large"style"width: 240px"placeholder"請選擇狀態"value-key"value"//value-key 與下面的ke…

How to implement multiple file uploads based on Swagger 3.x in Spring boot 3.x

How to implement multiple file uploads based on Swagger 3.x in Spring boot 3.x Projectpom.xmlOpenAPIConfigFileUploadControllerapplication.yaml Project pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://…

軟件壓力測試:測試方法與步驟詳解

隨著軟件應用的不斷發展&#xff0c;用戶對系統性能的要求也逐漸提高。在不同的負載條件下&#xff0c;系統必須能夠保持穩定、高效的運行。軟件壓力測試是一種驗證系統在各種負載情況下性能表現的關鍵手段。本文將詳細探討軟件壓力測試的方法和步驟。 1. 明確測試目標 在進行壓…

vue源碼分析之nextTick源碼分析-逐行逐析-錯誤分析

nextTick的使用背景 在vue項目中&#xff0c;經常會使用到nextTick這個api&#xff0c;一直在猜想其是怎么實現的&#xff0c;今天有幸研讀了下&#xff0c;雖然源碼又些許問題&#xff0c;但仍值得借鑒 核心源碼解析 判斷當前環境使用最合適的API并保存函數 promise 判斷…

2024年2月17日~2月23日周報

文章目錄 一、前言二、DDNet架構學習2.1 數據預處理2.2 網絡模型構建 三、基于深度學習地震數據去噪處理3.1 深度學習在地震數據去噪中的研究方向3.2 深度學習地震數據去噪流程3.2.1 數據集準備3.2.2 模型構建3.2.3 訓練網絡 3.3 基于DnCNN的地震數據去噪實驗 四、小結4.1 存在…

xxl_job系列---【GLUE(shell)模式如何傳多個參數,并被shell接收】

1.場景 我需要傳入多個參數&#xff0c;在執行任務時傳入的任務參數用逗號隔開&#xff0c;到shell中都被$1當做字符串接收到了&#xff0c;沒辦法&#xff0c;只能在shell中截取字符串&#xff0c;截成多個參數。 2.編輯入參 CRON:0 0 0 1 * ? #每月1號0點執行 點擊“任務管理…

Javascript數字精度丟失的問題

一、問題 0.1 0.2 0.3 // false 二、浮點數 “浮點數”是一種表示數字的標準&#xff0c;整數也可以用浮點數的格式來存儲 我們也可以理解成&#xff0c;浮點數就是小數 在JavaScript中&#xff0c;現在主流的數值類型是Number&#xff0c;而Number采用的是IEEE754規范中…

uniapp開發微信小程序跳轉到另一個小程序中

注意&#xff1a;一開始我的云上務工模塊是單獨的tabbar界面&#xff0c;但是小程序跳轉好像不能直接點擊tabbar進行&#xff0c;所以我將這里改成了點擊首頁中的按鈕進行跳轉 點擊這里進行小程序跳轉 目錄 基礎講解 uniapp小程序跳轉的兩個方法 調用說明&#xff08;半屏跳轉…

C++力扣題目 647--回文子串 516--最長回文子序列

647. 回文子串 力扣題目鏈接(opens new window) 給定一個字符串&#xff0c;你的任務是計算這個字符串中有多少個回文子串。 具有不同開始位置或結束位置的子串&#xff0c;即使是由相同的字符組成&#xff0c;也會被視作不同的子串。 示例 1&#xff1a; 輸入&#xff1a…

面試系列之《Spark》(持續更新...)

參考文檔及示例代碼均基于pyspark3.1.2 1.什么是RDD&#xff1f;2.job、stage、task如何劃分&#xff1f;3.什么是寬窄依賴&#xff1f;4.spark有哪幾種部署模式&#xff1f;5.spark中的算子分為哪些類型&#xff0c;舉例說明。6.cache、persist、checkpoint的區別&#xff0c;…

C++模板為什么不能聲明和定義分離

首先我們要直到C程序運行需要進行的四個階段。 預處理->編譯->匯編->鏈接 編譯&#xff1a;對語法語義分析&#xff0c;分析無誤生成匯編&#xff0c;頭文件不參加編譯&#xff0c;多個源文件是分開單獨編譯的。 鏈接&#xff1a;將多個obj文件鏈接合成一個&#x…

ubuntu20.04安裝webots仿真

ubuntu20.04安裝webots仿真 1.首先: wget -qO- https://cyberbotics.com/Cyberbotics.asc | sudo apt-key add - sudo apt-add-repository deb https://cyberbotics.com/debian/ binary-amd64/ sudo apt-get update sudo apt-get install webots .bashrc中添加環境變量:…

Sora----打破虛實之間的最后一根枷鎖----這扇門的背后是人類文明的晟陽還是最后的余暉

目錄 一.Sora出道即巔峰 二.為何說Sora是該領域的巨頭 三.Sora無敵的背后究竟有怎樣先進的處理技術 1.Spacetime Latent Patches 潛變量時空碎片&#xff0c;建構視覺語言系統 2.擴散模型與Diffusion Transformer&#xff0c;組合成強大的信息提取器 3.DiT應用于潛變量時…

關于在分布式環境中RVN和使用場景的介紹4

簡介 在前面的文檔中&#xff0c;我們介紹了RVN的概念&#xff0c;通過RVN可以解決的某類問題和使用技巧&#xff0c;以及處理RVN的邏輯的具體實現。在本文中&#xff0c;我們將要介紹關于如何使用RVN解決另一種在分布式系統中常出現的問題。 問題 假設我們創建了一個servic…

C語言—自定義(構造)類型

2.20&#xff0c;17.56 1.只有當我們使用結構體類型定義變量/結構體數組,系統才會為結構體的成員分配內存空間,用于存儲對應類型的數據 2.strct 結構體 一起作為結構體類型標識符 嘿嘿暫時先這樣&#xff0c;我會回來改的1、定義一個表示公交線路的結構體&#xff0c;要…

pikachu靶場-CSRF

CSRF: 介紹&#xff1a; Cross-site request forgery簡稱為"CSRF”。 在CSF的攻擊場景中攻擊者會偽造一個請求&#xff08;這個請求一般是一個鏈接&#xff09; 然后欺騙目標用戶進行點擊&#xff0c;用戶一旦點擊了這個請求&#xff0c;整個攻擊也就完成了&#xff0…