使用c++開發跨平臺的程式
使用c++開發跨平臺的程式
背景
在開發過程中,使用c++作為開發語言,通常被認為是痛苦的,囉嗦的,超長開發時間的.最近幾年有各種各樣的語言被廣泛使用,相對比來說c++不是那麼出彩.c++雖然年齡大,但是它不是坐以待斃的,它自己也在急劇變革,最近幾年,為了方便c++的開發,湧現出非常多的工具.我結合自己的工作經驗,打算寫點東西,介紹一下.
C++誕生初期解決了很實際的問題,但是隨著時代的變革c++遇到了新的情況.
首先就是包依賴管理,c++並沒有在這上面有所約束,導致開發在管理依賴包的時候,非常的困難,有時候可以說是一團糟.c++的處境,有點像nuget出現之前的c#,maven出現之前的java.不過現在這個也得到了解決.微軟推出了vcpkg.此工具完全解決了這個問題.當然,針對c++包管理,還有其他的工具,此處,我只推薦vcpkg.理由如下:1.它切合了現代c++的開發流程.2.它真的很好的管理了包以及包依賴,以及標頭檔案.3.它易於使用.在開發過程中每個平臺的操作方式都是一致的.
其次專案組織方式,c++並沒有在這上有所約束.好吧,所處的時代的確沒有考慮到未來的情況.我這裡選擇cmake. 段子 A:我寫c++程式碼, B:具體的工作是? A:你具體指的是寫c++程式碼,寫c++程式碼的程式碼,以及寫c++檔案的程式碼?....雖然是段子,不過cmake就是c++檔案的程式碼.現在也算是c++開發的標配了.
再次開發工具,此處有很多選擇,vs和vscode,以及clion.我選擇了clion.
最後開發的作業系統,既然是開發跨平臺的程式,開發平臺也需要選擇一下,我選擇了win10.沒有選擇linux桌面系統.理由簡單,windows工具太多了.我現在年齡已經很大了.已經過了需要使用作業系統來體現自我逼格的階段了.我無意爭論那個系統好與壞.我現在需要的是生活的時間.(然而碼農最缺少的就是時間,其實最主要原因是工作交流都使用win系統)再說以上工具都是跨平臺的,完全可以無縫的移植到win或者linux桌面上去.真正的完全無縫.操作方式都不帶任何變化的.
總之本文就是使用clion+cmake+vcpkg的開發現代的c++跨平臺程式.
專案的基本組織方式
專案是在win10中編寫的,我不怕編寫東西,我只是怕出了問題,不知道問題所在.所以編譯和除錯也佔據很重要的地位,都是遠端操作的.
如果畫一個圖,可以如下表示.
以上可以看出,除去除錯是在windows上以外,其餘的編譯和除錯,都不行在各個平臺再進行一遍.實際開發過程中,編譯和除錯一般只需要遠端在linux下進行,win系統一般不需要任何改動.這完完全全得益於cmake和vcpkg(真的是強烈推薦,可以說,如果沒有vcpkg,我不會寫這個東西).
囉嗦了這麼多,現在終於要進入正文了.
環境搭配
Linux環境準備
準備一個linux操作環境,我準備是ubuntu18.04 .
在Ubuntu上安裝git,cmake,gcc7.3.0.這三個網上的安裝教程多如牛毛.這裡就不復述了.現在我把我機器上的安裝環境截圖如下.
安裝vcpkg.
1. 建立一個目錄/vcpkg
2. 輸入指令git clone https://github.com/microsoft/vcpkg
3. 進入目錄/vcpkg,
4. 編譯sudo bash ./ bootstrap-vcpkg.sh,會生成一個可執行檔案vcpkg,不用安裝到任何其他的位置,讓它留在生成的地方就好.
5. 定義環境變數 VCPKG_ROOT="/vcpkg"
6. 安裝gdb和ssh,這個是clion遠端除錯要使用的.
Windows環境準備
我使用的是 win10.安裝了git,cmake,vs2019.
再安裝vcpkg.定義一個環境變數VCPKG_ROOT="/vcpkg"
管理包
現在使用vcpkg安裝一些必要的c++包.
比如,我安裝了boost包.
vcpkg install boost
檢視一下.vcpkg list或者vcpkg list boost,部分截圖如下
我只是為了嘚瑟一下而已,列表是在太長了.嘚瑟是程式設計師的必要需求.
Vcpkg的基本檔案結構
vcpkg會在VCPKG_ROOT所指向的目錄底下儲存所有的包,
linux示意如下.
Windows示意如下
vcpkg主要使用的命令就是
vcpkg search xxxx ##搜尋一個特定的c++庫
vcpkg list xxxx ##顯示已經安裝的c++庫.
vcpkg install xxxx ##安裝一個特定的庫
如果某個庫檔案有,而vcpkg沒有收錄.可以去研究一下ports/ 目錄底下的各個資料夾.裡面的檔案指示vcpkg應該如何搜尋特定的包.如果檢視大部分portfile.cmake檔案,就發現,vcpkg大部分庫來自於github.這裡只是提提,就不展開了.大家自己研究.
與Clion互動
安裝clion,我這裡使用的是2019.1.3版本.安裝就不復述了.廢話一句就是,有錢捧錢場,沒錢的捧人場.
我們使用vcpkg安裝好了各種庫,目的是為了方便的使用,讓vcpkg幫我們管理各種各樣的標頭檔案啥的.我們只需要放肆的編寫bug,嗯…我們只需要專注於實現業務邏輯.這一切有一個統一的介面呈現給我們,這個介面就是clion.
如果要新增一個新的庫檔案一般就是如下流程
vcpkg install xxxx ----àclion更新cmake-à放肆的使用新安裝的庫.-à編譯-à除錯.
以上各個步驟,第一步需要手動輸入一個指令外,第二步需要更改一點cmake檔案.除去以上步驟以外,剩餘的所有步驟都不需要手動干預.這在vcpkg出現之前是不敢想象的.之前光引入標頭檔案就勸退了多少人呀.如果再加上編譯,連線庫檔案等等動作…c++又名c艹是非常有道理的.
現在第一步手動輸入指令與第二步更改cmake檔案,應該都不算是負擔了,如果還覺得是負擔,可以說只能等AI自動程式設計來拯救了.(我用臉滾了一晚上鍵盤,編寫處理新一代作業系統windoors,不是夢).
下面通過一個簡單的專案把專案建立到除錯,再過具體一點.
Hello world
現在的主角就要轉向clion了.我打算建立一個簡單的c++程式,這個程式可以在linux上編譯除錯,同時也可以在win上編譯除錯.
我使用clion建立了一個新專案,截圖如下
以下,是我的編譯工具鏈的設定.找到選單 File->Settings->Build,Execution->Toolchains.截圖如下
找到File->Settings-> Build,Execution->Deployment.配置一個遠端linux主機.截圖如下
因為要遠端編譯,必然的要把本地的原始碼傳送到遠端linux裡去.這裡就是指示clion如何對應本地專案目錄和遠端目錄的.其中Mapping指定了如何對應.大家可以根據自己的需要設定.下圖我設定成立專案根目錄對應著/root/clionproject/test/.
更改以上兩個,下面要配置clion當中的cmake了.這裡才是真正使得clion和vcpkg互動起來.找到File->Settings-> Build,Execution->CMake,在ToolChain選擇我們剛剛設定的工具鏈之一.
其中CMake options要填入:
-DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake
這樣vcpkg就可以和clion互動起來了.clion使用vcpkg.cmake檔案,此檔案會指導cmake來尋找vcpkg管理的各種庫檔案,並指導clion使用它們.
依照同樣的方式創造一下配置,使得clion也可以在vs下編譯此程式.
下面我編寫一個簡單的hellow world程式,此程式使用boost,編寫一次,分別在linux和windows下編譯(一次編寫,處處編譯),並打印出此時的作業系統型別.
1. 在Clion中建立一個新的專案:略
2. 新增Boost庫到專案中
使用以上配置使得條件boost庫變的簡單.
在CMakeLists.txt中編輯如下內容
3. 新增業務邏輯
裡面有兩個檔案main.cpp.
OperationSystem.hpp
邏輯都超級簡單,就不復述了.
4. 最後編譯.只需要選擇配置,然後點選編譯就可以了.截圖如下
以上截圖是linux,windows也是一樣的.就不截圖了.
一次編輯,處處編譯
考慮以上過程,如果一套程式碼你再一臺機器編輯好.比如在windows編輯好.然後複製到linux底下編譯一下,有沒有需要更改的地方?從以下幾個方面檢查一下
Cmake:一套配置檔案在linux和windows底下是相同的,不用更改.
依賴庫:都統一使用vcpkg管理依賴庫.在linux和windows下是相同的,也不用更改.
專案程式碼:也不用更改.
編譯命令:都是cmake自動操作的.也是一樣的.
發現:基本上做到了一次編輯,處處編譯.由於c++歷史的原因,想要完全做到此特點,也著實難辦.對於一些特殊的需求,特別是硬體需求,低層需求,還是要使用預編譯符號或者cmake來針對不同作業系統,區別對待.雖然不能不能做到如netcore一樣真正一次編輯處處編譯.畢竟那是有大廠支援的.可以說,通過以上的介紹,基本上實現了”一次編輯,處處編譯”.一套程式碼中只有非常少量的幾行程式碼需要針對作業系統特殊處理.
另一個由”處處編譯”帶來的好處就是可以自動打包.通過git上傳程式碼至程式碼庫,然後在目標機器中使用自動打包工具自動編譯.
C++工具
格式化工具
Clion內建了一個clang格式化工具.可以使用它來格式化自己的程式碼.這個不多贅述了.
記憶體洩漏
很多語言都內建了自動垃圾回收所謂GC,比如net,go等.但是有很多時候,由於裝置或者效率的要求,使得執行環境不適合GC.但是記憶體回收依然十分的重要.而C++的記憶體洩露卻總是使得各位碼農花費很多時間去避免.如剃刀一般剃光了很多人的頭髮.
C++發展了很多工具,來避免記憶體洩露,如智慧指標.但是由於庫或者歷史的原因,使得智慧指標不能覆蓋全部程式碼.現在有工具來解決這個問題了.有時候,我總是覺得,造東西不怕,我是怕出問題了卻不知道在哪(所以測試很重要).谷歌出了一套工具可以偵測記憶體洩露.名字叫做sanitizers.
下面我先截圖一下,看看如果發生了記憶體洩露大約給出一個什麼樣子的提示:
在上圖中可以發現,在程式退出的時候,它列印了一些資訊,明確的指出哪裡(具體到了行)洩露了,洩露了多少位元組.由於GCC內建了這個sanitizers.所以這裡我直接使用了GCC內建的sanitizers.
下面我將使用此sanitizers.
程式很簡單,我改造main.cpp,截圖如下
上段申請了一段記憶體並沒有釋放.
在cmake檔案中新增以下一句
SET(CMAKE_CXX_FLAGS "-fsanitize=address")
它目的就是告訴編譯器,增加一個flag sanitize=address .
現在我在linux環境中編譯執行,得到如下截圖.
可是問題來了.如果同樣的程式碼在windows底下執行,就會獲得如下錯誤.
好在記憶體洩露的檢查只需要一次就好.所以我一般就修改cmake,使得它只會在gcc編譯器下使用sanitize.而在msvc下就不使用此flag.不要亂立flag.
在程式結束的時候報告記憶體洩露的位置,msvc很早之前就有了.可以參考此文章. https://docs.microsoft.com/en-us/visualstudio/debugger/finding-memory-leaks-using-the-crt-library?view=vs-2019
同時,如果使用 clang-cl來替代cl.也可以使用sanitize,但是有某些功能上的限制.如果詳細檢視微軟的文章,就會發現,其實微軟的CRT記憶體洩露偵測,更靈活全面.功能更強大.
以上程式碼都是簡單的演示,程式碼邏輯非常簡單.原始碼我就不上了.有心實驗者按照圖片自己稍微敲敲吧.
谷歌的sanitize還有一個ThreadSanitizer,專門用來檢測資源競爭和死鎖的.使用也是一樣