在WebClient請求期間發生異常C?
提案里把「錯誤」分成五類
機器抽象被破壞:建議直接 terminate代碼 bug:建議使用 Contracts(默認 terminate)OOM:建議用 new(nothrow) 和 try_ 系列函數可恢復的錯誤:建議拋異?;蛘叻祷劐e誤碼部分成功:建議當作成功C/C++ 語言都是基于一臺抽象機器定義的,如果這樣的抽象被破壞根本沒得玩,只能退出。例子是「棧」耗盡:你不可能避免、也不可能解決。
代碼 bug 就是類似「前置條件不滿足」的情況,C/C++ 傳統上的處理辦法是「需要滿足某某條件,如果不滿足是未定義行為」(C++ 一度誤入 OO 歧途跟風搞出 std::invalid_argument 這種就讓它隨風而去吧),用戶代碼一般推薦 assert。提案中建議統一交給 Contracts 處理我覺得非常好,因為這樣的函數不會涉及未定義行為,同時違反時會有明確的報錯信息,對懶人友好。
「部分成功」即類似「傳給 snprintf 的緩沖長度不夠用」這種,只要有良好的文檔定義此時的行為,這種情況完全可以當作成功,而不是錯誤。
剩下的兩個就是大頭:「異常 / 錯誤碼」和「OOM」。
靜態異常是這個提案的大頭。
說到底,沒有異常的 C++ 根本不是標準 C++,標準庫也依賴于異常。很多 C++ 工程和規范不接受異常,主要是因為:
最終二進制體積膨脹,同時運行時需要額外的空間和時間開銷運行時空間和時間開銷不確定新的 throws 函數 + std::error 的組合是披著異常外衣的錯誤碼,內在有點像 Swift 里的一種錯誤匯報范式:
提案中的代碼類似這樣
std::error 可以理解為兩個指針,所以可以和正常的返回值共享同一個通道(甚至寄存器):
payload:可以是錯誤碼,也可以是異常指針 exception_ptr。domain:類似 std::error_category 性質,區分不同領域的錯誤碼,可以是哈希也可以是指針如果 throws 函數中有動態異常試圖越界,則通過某種機制映射為對應的 std::error,在 payload 中保存該異常的指針。
如果在普通函數中所調用的 throws 函數拋出異常,則通過某種機制從 std::error 映射為對應的動態異常對象拋出。
這樣一來,靜態異常和普通的返回值無異,不再依賴堆分配、RTTI,只是語法上與「傳統」動態異常相似。同時還兼顧了已有代碼,可以說是個非常不錯的解決方案了。
You don't pay for what you don't use.When you do use it you can’t reasonably write it more efficiently by hand.最后說一下 OOM 吧。
首先,我一直覺得「OOM 不可恢復」并不成立,這一點提案中也提到了「OOM 不等于內存耗盡,只是無法獲得指定大小的內存,請求 1T 內存失敗,換 1G 內存可能就成功了」。例如圖片解碼時 OOM 可以做 subsampling。
提案中建議 new 失敗統一 terminate:
如果需要舊行為可以替換 new handler 拋 std::bad_alloc如果需要自行處理可以用 new(nothrow)標準庫中提供 try_ 系列函數,例如 std::vector::reserve 對應 std::vector::try_reserve默認構造函數默認 noexcept我個人對此頗有微詞,非常希望這一部分得不到通過:
自行替換 new handler:要求改全局的東西總有種壞味道,也讓庫作者、調用者很為難new(nothrow):這種特殊化反而增加工作量,new 一個對象還得同時用兩種不同的錯誤處理機制處理問題(分別處理構造函數拋出的異常和 new 返回的空指針)try_ 系列函數:又是「異常改錯誤碼」的變體,如果納入上文的 throws 函數范疇似乎更好有趣的是,提案中針對是否需要特殊化 OOM(是否需要區分開頭的第 3 和第 4 類錯誤)分別列舉了正反雙方觀點。不支持特殊化 OOM 的基本都是「既然一定是顯式申請的,如何處理應該由調用方決定」「有某某解決方案」這種客觀理由;支持特殊化 OOM 的理由在我看來大多很任性很諷刺:
難處理、很多人處理錯了恢復需要額外的特殊處理(無法直面事實)(按:前面括號里的內容不是我加的)某些情況下不可能發生這樣的錯誤你自己標準里的解決方法就有不一致的地方直接忽略的話會帶來很多實現上的好處最后一點才是正經的理由,如果說實現上帶來方便優化等好處,特殊處理 OOM 似乎確實說得過去。而其它理由基本都只是在抱怨、逃避問題,非常欣賞作者的那個括號:無法直面事實。