“越來越像新語言的 C++,我與它結緣、痴迷、深耕的 14 年!”

“越來越像新語言的 C++,我與它結緣、痴迷、深耕的 14 年!”

【CSDN 編者按】提到C++,不少人會想起Linus吐槽其是一門糟糕的程式語言,也有人覺得不斷髮版的C++,越來越像一門“新”語言。

本文主人公祁宇從事C++開發工作已有14年,從結緣再到一頭扎進去深耕,對C++的興趣和熱愛隨著版本的更迭代有增無減。在《新程式設計師004》的“我是程式設計師”專題中,祁宇回顧了自己與C++結緣、痴迷其中一路深耕的故事。這既是他的程式人生,也是C++的發展演進歷程。

作者 | 祁宇 責編 | 張紅月

出品 | 《新程式設計師》

“越來越像新語言的 C++,我與它結緣、痴迷、深耕的 14 年!”

祁宇,purecpp社群發起人,《深入應用C++11》作者,開源庫rest_rpc、cinatra、iguana、Feather、ormpp作者。熱愛開源和現代C++,樂於研究和分享技術。

緣起C++11

2008年我畢業後第一份工作就是C++開發,那時候是經典C++98/03標準,C++新標準還沒有出來。而我對C++談不上熱愛,甚至相比之下,覺得C#用起來比C++要舒服得多,比如C#有GC(Garbage Collector)機制,沒有記憶體洩漏的煩惱;有Lambda,程式碼也很簡潔,不像C++那樣需要定義函式物件;C#還有執行緒和鎖,而C++多執行緒還只能用系統API,導致寫跨平臺程式很麻煩……諸多問題。

在我寫了三年C++程式碼後,ß∂C++的一個里程碑——C++11誕生了。C++11提供的新特性有一百多項,讓人眼花繚亂。C++11解決了之前C++98/03的大部分問題,比如用智慧指標避免記憶體洩漏,Lambda擺脫了函式物件,std::bind統一了之前的bind1st、bind2nd,提供了執行緒庫和鎖,還有魔法般的可變模版引數等等。總之,C++11讓C++煥然一新,變得很“現代”了,正如C++之父Bjarne Stroustrup所言:“C++11看起來像一門新的語言。”

C++11相比C#、Java等其他語言,從易用性角度來說已經毫不遜色了。而我也被這些新特性深深地吸引住,樂此不疲地研究和實踐它們。在這個過程中,我對現代C++的興趣被激發出來,第一次發現原來它的世界裡有這麼多好用又有趣的東西。

比如,C++11可變模版引數,對引數做了泛化,表示任意型別和任意個數的引數,這是C++11之前沒有的。那麼,該如何解析可變模版引數包裡的引數呢?一種經典的寫法是透過模版遞迴來實現。

void print(){}template

本文節選自《新程式設計師004》紙刊+電子刊同步上市

建立現代C++開源社群

現代C++雖然在語言層面的開發效率不遜於其他語言,但在基礎庫方面就差太多了。在2014年,C++連個像樣的HTTP庫都沒有,而這在Java、C#等語言中早就有了。正是這些基礎庫的缺失,極大地阻礙了C++的開發效率。出於對C++的熱愛,我希望能開發很多實用的基礎庫來完善它的生態圈。於是在眾多粉絲的支援下,2015年我決定成立一個現代C++開源社群——purecpp。org。在社群中開發C++基礎庫,期望來提升C++開發效率。我陸續開發了HTTP庫cinatra、RPC庫rest_rpc、序列化庫iguana、ORM庫ormpp、Web框架Feather等等,後來還在GitHub上建立了purecpp-org專案列表,把社群裡的優秀開源專案聚合起來,便於C++開發者選擇。

完善C++生態圈,讓更多人用上好的C++基礎庫,這是我創辦C++開源社群的初衷。也欣喜地看到社群開源專案被越來越多的公司採用,比如cinatra已經有十幾家公司實名使用;rest_rpc被博世汽車車聯網團隊使用,已經跑在數百臺汽車的嵌入式系統中;一些券商在使用ormpp訪問資料庫等等。

purecpp是一個很純粹的C++社群,創辦至今沒有做任何的商業化,連廣告都沒有,專注於C++新技術分享和C++開源專案。從2018年開始,每年都會組織Pure C++大會,大會都是免費的,相關費用主要靠贊助,有時候是自己出資解決,社群大會也得到了很多熱心朋友的幫助與支援,正所謂“C++不孤,必有鄰”!

C++新標準與技術創新

有很多人對C++新標準不“感冒”,對新技術也持懷疑態度,甚至認為C++新標準不過是加了一些語法糖而已,認為新標準的一些寫法是奇技淫巧,這其實是非常大的誤解。誠然,C++新標準確實引入了一些語法糖來簡化程式碼的編寫,但更多是彌補了語言上的一些缺失,提高C++的易用性,提升安全性和效能,甚至改變程式設計理念。

一個典型的例子就是constexpr特性,它是在C++11中被引入的,用作編譯期計算。透過它,可以用很簡單的方式實現編譯期計算,不用再像以前一樣需要透過模版超程式設計來實現,使用難度大大降低。

以一個編譯期計算階乘的例子來展示constexpr的作用,透過圖1我們可以看到,在C++98/03中,需要透過模版遞迴來實現編譯期計算,程式碼比較晦澀。而在C++11中可以直接透過一個constexpr函式來實現編譯期計算,即用constexpr函式來實現非模版的編譯期計算。這個函式除了宣告為constexpr外和編寫一個普通函式沒什麼區別,這樣就大大降低了編譯期計算的難度。

“越來越像新語言的 C++,我與它結緣、痴迷、深耕的 14 年!”

圖1 編譯期計算階乘示例對比

不過,C++11中constexpr函式存在一些約束,它的函式體只能是一個表示式,不能定義變數,不能有迴圈語句等。在C++14中消除了這些約束,允許定義變數、允許有迴圈,讓constexpr函式真正和普通函式的寫法無異。

所以,C++14讓constexpr的易用性得到了提升。

我在2021年Pure C++大會上講過如何使用constexpr編譯期計算來提升程式效能,當時分享的一個用constexpr做效能最佳化的案例,透過constexpr編譯期計算將程式效能提升了三十多倍。

管中窺豹,從constexpr這個新特性可以看到C++新標準演進的一個重要趨勢:讓C++變得更加容易,效能更好。

正如purecpp社群的Slogan“Newer is Better!”我非常推崇使用C++新標準和新技術,因為C++新標準帶來的新技術往往會促進技術創新,技術創新帶來改變。我在2020年Pure C++大會上介紹了一種新的外掛化開發方法,就是透過C++的新技術解決以前經典的C++外掛化開發中外掛需要派生於某個固定介面的問題。新的外掛化方法完全擺脫了繼承的約束,可以自由地在外掛中寫任意方法,這種新技術已經用在開源專案Ray當中了。

關於C++新標準帶來的技術創新另外一個典型的例子就是編譯期反射,我近期做了一些技術驗證,基於C++20實現的編譯期反射序列化庫的效能是ProtoBuf和Msgpack的十幾倍,這個測試結果讓我有些吃驚。沒想到原來基於編譯期反射的序列化效能可以超越被廣泛使用的序列化庫這麼多,這就是C++新標準技術創新的威力!

如果說C++11是C++的一個里程碑,那麼C++20就是現代C++的一個里程碑。因為它引入了非常重要的幾個特性,而這將改變C++的程式設計理念和模型,當然也讓C++變得更加好用,比如Concepts、Modules、Coroutine、Ranges等。以協程(Coroutine)為例,它可以讓我們用同步方式寫非同步程式碼,徹底擺脫了回撥地獄(Callback Hell),非同步回撥模型將被協程代替。用C++20新標準,失去的是彎彎繞繞的Callback,得到的是簡單直接的協程。協程讓非同步變得簡單,在未來3~5年,C++網路庫的協程化將是大勢所趨。

Modules讓我們不用再引入include標頭檔案了,而是像其他語言一樣使用import庫就好,同時它還會大大提升編譯速度。我們做過一個測試,將async_simple[2] 模組化後,編譯速度提升了45%,效能也略有提升。ModulesABI相關的工作也正在做,可以說,Modules帶來了C++統一包管理的曙光。

C++2a帶來的使命感

C++新標準的演進速度比較快,從C++11開始保持著三年一個版本的迭代速度,後面還有C++23/26/29。而未來C++標準將提供更多好東西,比如Executors、Networking、編譯期反射等。雖然新標準推出比較快,但基於新標準的庫卻跟不上,比如協程庫,目前除了async_simple外,能用的無棧協程庫幾乎沒有,這也是C++的遺留問題。

想象一下,如果把協程、編譯期反射、Executors結合起來開發一些高效能易用的網路庫,那是多麼有意義的一件事!我也正在做這方面的工作,希望能儘早推出到C++開源社群當中。

研究C++2a新標準、新技術及其應用場景,創造符合應用需求的基礎庫讓我有一種使命感,我會和C++社群的朋友們一起繼續努力推出更多實用的、基於C++新標準的基礎庫給C++開發者使用,完善C++生態圈。

頂部