巢狀異常

Version >= C++ 11

在異常處理期間,當你從低階函式捕獲一般異常(例如檔案系統錯誤或資料傳輸錯誤)時會出現一個常見的用例,並丟擲一個更具體的高階異常,表明某些高階操作可以不執行(例如無法在 Web 上釋出照片)。這允許異常處理對高階操作的特定問題做出反應,並且只允許錯誤訊息,程式設計師在應用程式中找到發生異常的位置。此解決方案的缺點是異常 callstack 被截斷並且原始異常丟失。這迫使開發人員手動將原始異常的文字包含到新建立的異常文字中。

巢狀異常旨在通過將描述原因的低階異常附加到高階異常來解決問題,該異常描述了在這種特定情況下它意味著什麼。

由於 std::throw_with_nestedstd::nested_exception 允許巢狀異常:

#include <stdexcept>
#include <exception>
#include <string>
#include <fstream>
#include <iostream>

struct MyException
{
    MyException(const std::string& message) : message(message) {}
    std::string message;
};

void print_current_exception(int level)
{
    try {
        throw;
    } catch (const std::exception& e) {
        std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n';
    } catch (const MyException& e) {
        std::cerr << std::string(level, ' ') << "MyException: " << e.message << '\n';
    } catch (...) {
        std::cerr << "Unkown exception\n";
    }
}

void print_current_exception_with_nested(int level =  0)
{
    try {
        throw;
    } catch (...) {
        print_current_exception(level);
    }    
    try {
        throw;
    } catch (const std::nested_exception& nested) {
        try {
            nested.rethrow_nested();
        } catch (...) {
            print_current_exception_with_nested(level + 1); // recursion
        }
    } catch (...) {
        //Empty // End recursion
    }
}

// sample function that catches an exception and wraps it in a nested exception
void open_file(const std::string& s)
{
    try {
        std::ifstream file(s);
        file.exceptions(std::ios_base::failbit);
    } catch(...) {
        std::throw_with_nested(MyException{"Couldn't open " + s});
    }
}
 
// sample function that catches an exception and wraps it in a nested exception
void run()
{
    try {
        open_file("nonexistent.file");
    } catch(...) {
        std::throw_with_nested( std::runtime_error("run() failed") );
    }
}
 
// runs the sample function above and prints the caught exception
int main()
{
    try {
        run();
    } catch(...) {
        print_current_exception_with_nested();
    }
}

可能的輸出:

exception: run() failed
 MyException: Couldn't open nonexistent.file
  exception: basic_ios::clear

如果你只使用從 std::exception 繼承的異常,則甚至可以簡化程式碼。