回首有關C++協程的往事,真是猶如一段十分漫長的旅程。
早在2013年,我們就官宣了一個有關」可恢復函數」的早期預覽版本,接下來在2014年,我們添加了/await開關並提交了C++標準化建議的初始版本。2015年,我們提交了修訂版本。後來,通過Visual Studio 2017和Visual Studio 2019,我們都在持續地跟蹤有關協程TS(Technical Sepecificaion)的內容更新。2019年,隨著協程完全實現被納入到C++標準中,我們終於可以宣布了:Visual Studio 2019 v16.8將完全支持C++20協程特性。
最終通過標準化程序成為C++20一部分的協程支持,與早期的提議草案以及我們在/await開關下在MSVC中獲得的實驗版本協程有所不同。
我們在實現v16.8中的協程支持時,考慮了兩個重要,但是相互矛盾的目標。
1. 提供一個嚴格遵循C++標準的協程實現,使得用戶可以編寫和使用可移植代碼。
2. 確保那些使用實驗版本協程的用戶可以毫不費力地升級到v16.8而無需改動他們的代碼。
隨著提案的更改,我們會儘可能增加新的支持,而不破壞針使用早期協程版本的代碼。
這當然不是標準的:它仍然接受所有舊的關鍵字,名稱和籤名,和上面的第一個目標對應。
與我們在/await下實現的原始版本相比,還存在少量的行為更改,例如一個promise對象的構建方式。這些可能導致先前可以通過編譯的程序變得無法編譯或表現出與之前不一樣的行為。
使用比C++17更新的編譯器語言版本模式時,就能實現對C++20協程的支持,同時,不帶有對舊版TS的支持。現在,這是/std:c++latest,在添加了C ++ 17之後,它將繼續進入編號版本的開關。使用這種語言開關進行編譯且不帶/await時,你就可以獲得對C++20協程的嚴格支持,我們在頭文件中和std名稱空間中提供了庫級別支持。此模式將針對早期建議中的非標準代碼會給出錯誤提示,例如,使用了bare await關鍵字或返回bool的initial_suspend函數,並且僅在其與早期實現不同時才支持這種標準行為。
協程的早期使用者可以繼續使用/await開關和任何語言版本的開關(包括/std:c++latest)來編譯其非標準化的代碼,並繼續使用實驗性頭文件和命名空間。只要不破壞兼容性,我們都會在此模式下添加那些缺少的標準功能並提供錯誤修復。
我們建議現有的協程使用者儘快地遷移到標準版協程,新用戶應優先使用標準模式而不是/await。 針對現有用戶,我們將繼續支持/await開關,但是將來的協程開發版本將處於標準模式,並且在那裡將實現新功能。除了一些極端的情況,將項目從/await遷移到C++20應該是一個簡單的過程。
v16.8版本中引入了一些有關協程的新功能和改進:
> 對稱轉移(Symmetric transfer)
> 無操作協程(No-op coroutines)
> 協程Promise構造函數參數
> 定義良好的行為,使異常控制流從協程中退出
> 標準返回對象轉換行為
> 優化後的調試體驗
> 通用幀布局,以提高與其他供應商的兼容性
> 大量錯誤修復
無操作協程和大多數錯誤修復程序也已在/await下實現,但大多數這些更改僅在以標準模式構建時可用。在本文的其餘部分,我們將仔細研究其中一些新功能的細節以及Visual Studio有關協程的下一個階段版本。
這是C++20協程支持的最後兩個缺失的部分。對於對稱轉移,協程可以指示協程句柄,以便另一個協程在掛起時立即恢復。這是通過使用coroutine_handle的返回類型定義協程await_suspend函數來完成的,如下圖所示:
在標準模式下,此掛起和恢復操作可以在不將另一個幀引入到調用堆棧的情況下工作。這樣就可以在協程之間進行不限大小的數據傳輸,而不會冒堆棧溢出的風險。
v16.8版引入了一些用於協程的新調試功能。
修正了協程步入的一些問題,尤其是和選項」Just My Code」一起工作的時候。
你還可以在協程程序中展開棧指針。通過這個特性,你可以看到一些公開的數據,例如協程參數值和promise類型的成員(僅限標準協程)。
我們還更改了許多由編譯器生成的符號的名稱,以更好地與調試器的表達式求值一起使用。有了它們,就可以更容易在即時窗口或監視窗口中使用,也可以作為條件斷點使用。如下圖所示:
在標準C++ 20模式下,協程框架有了新的內部表示形式。這暴露了對協程非常重要的框架部分,例如如何以廠商之間通用的方式恢復或銷毀它。一個供應商生產的目標文件或庫中生成的協程可能會被另一供應商使用。這並不意味著完整框架的布局在各供應商之間是通用的,甚至不能保證在編譯器版本之間是穩定的,但是它確實標準化了(儘管是非正式的)標準庫類型std::coroutine_handle與基礎協程框架對象之間的接口,並在從庫中公開或使用協程時應有助於提高兼容性和靈活性。我們還引入了在Clang使用相同內置函數的支持,從而實現了更好的頭文件級兼容性。
當前,不同編譯器廠商之間的協程支持級別有所不同,但大部分正在不斷提高。隨著C++20支持在各個編譯器中廣泛推廣,我們希望這將變得更加有用和重要。我們致力於為協程提供通用,穩定的ABI,以使不同版本之間的接口儘可能實現無縫升級。
C++20中的協程還存在一些限制。雖然已經採用了核心語言功能,但是標準庫中沒有真正的協程支持。好消息是,我們預計很快就會有改變,在下一個C++語言版本中將對協程提供更廣泛的庫支持。
我們針對C++ 20協程的下一階段的目標是不斷改善調試體驗。這種情況的一個方面是,這是一個更自然的趨勢,使開發者可以更容易跟蹤協程執行,就好像它是普通的同步功能一樣。我們還在尋找改進的協程句柄可視化功能,以實現輕鬆查看協程運行狀態。
協程來了,老哥們是否準備好了呢?
還是,和我一樣,繼續寫寫HelloWorld,混混日子?
Microsoft Visual C++團隊的博客是我非常喜歡的博客之一,裡面有很多關於Visual C++的知識和最新開發進展。大浪淘沙,如果你對Visual C++這門古老的技術還是那麼感興趣,則可以經常去他們那(或者我這)逛逛。
本文來自:《C++ Coroutines in Visual Studio 2019 Version 16.8》