使用clion+cmake+vcpkg的開發現代的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,專門用來檢測資源競爭和死鎖的.使用也是一樣的.