2013-06-05 46 views
1

我寫了一個例程來存儲回溯和行號和文件名等。這樣做的目的是在拋出異常時存儲這些數據。然而,我面臨的問題是我的例程將從catch塊中調用,並且它最終會將追蹤存儲到catch塊。不是很好。我只需要添加回溯到拋出異常的地方。我不能(顯然在try塊內部調用它,因爲在那種情況下,即使在沒有拋出異常的情況下,我也將最終存儲回溯)。我也可以總是將回溯存儲到try塊的末尾並在catch塊內訪問它;但是無法知道try塊的哪一行會拋出異常。因此,throw函數似乎是添加例程調用的好地方。但我不知道該怎麼做。請幫幫我。在C++中編寫自定義異常類時,如何重載throw函數?

如果我的策略似乎有內在的錯誤,請隨時爲我指出一個更好的解決方案。如果問題本身不明確,請留下評論。

P.S.我編寫了自定義的異常類,以從std :: runtime_error繼承。

+1

你是否負責拋出這些異常?你可以從'std :: exception'派生類,在它們的構造函數中產生一個跟蹤。 –

+0

@DrewDormann我的確是派生自'std :: runtime_error',它是'std :: exception'的子類。你能否詳細說一下'從std :: exception派生類,它們在構造函數中產生一個trace' – Chani

+0

哪個平臺和編譯器? – Nicholaz

回答

4

沒有「拋出函數」,您可以覆蓋。投擲是由C++實現處理的,並且沒有標準的方法來爲每個throw插入任何類型的代碼。

相反,你可以做的是讓你的異常類型在構造時存儲當前回溯。

std::string get_backtrace() { 
    return "this is the backtrace..."; 
} 

struct backtrace_exception : public std::exception { 
    std::string b; 

    backtrace_exception() : b(get_backtrace()) {} 
}; 

int main() { 
    try { 
     throw backtrace_exception(); 
    } catch(backtrace_exception &e) { 
     std::cout << e.b; 
    } 
} 
+0

不,有一個他可以覆蓋的拋出函數,請參閱我的答案,下次打樣 –

1

你不能重載throw操作符。更常見的解決方案是定義一個宏,該宏用回溯記錄打包異常。例如這樣的:

#include <string> 
#include <iostream> 
#include <sstream> 
#include <exception> 
#include <stdexcept> 
#include <type_traits> 

template <typename BaseException> 
class backtraced_exception : public BaseException { 
    private: 
    std::string backtrace; 
    public: 

    template <typename... Args> 
    backtraced_exception(const char* aFilename, int aLineNum, Args&&... args) : 
     BaseException(std::forward<Args>(args)...) { 
     std::stringstream ss; 
     ss << "From '" << aFilename << "' at line " << aLineNum << ":\n" 
     << BaseException::what(); 
     backtrace = ss.str(); 
    }; 

    backtraced_exception(const std::exception& e, const char* aFilename, int aLineNum) : 
     BaseException(static_cast<const BaseException&>(e)) { 
     std::stringstream ss; 
     ss << "From '" << aFilename << "' at line " << aLineNum << ":\n" 
     << e.what(); 
     backtrace = ss.str(); 
    }; 

    virtual ~backtraced_exception() noexcept { }; 

    virtual const char* what() const noexcept { 
     return backtrace.c_str(); 
    }; 
}; 

#define THROW_WITH_BACKTRACE(EXCEPTION, ARG1) throw backtraced_exception<EXCEPTION>(__FILE__, __LINE__, ARG1) 
// ... and you can create more macros for more arguments... 

#define CATCH_WITH_BACKTRACE(EXCEPTION, EXCEPT_NAME) catch(backtraced_exception<EXCEPTION>& EXCEPT_NAME) 

#define RETHROW_WITH_BACKTRACE(EXCEPT_NAME) throw backtraced_exception< std::decay< decltype(EXCEPT_NAME) >::type >(EXCEPT_NAME, __FILE__, __LINE__) 

使用方法如下:

int main() { 
    try { 
    try { 
     try { 
     THROW_WITH_BACKTRACE(std::runtime_error, "This is an example!"); 
     } CATCH_WITH_BACKTRACE(std::runtime_error, e) { 
     std::cout << "First caught this exception:\n" << e.what() << std::endl; 
     RETHROW_WITH_BACKTRACE(e); 
     }; 
    } catch(std::runtime_error& e) { // can also catch normally. 
     std::cout << "Got this exception:\n" 
       << e.what() << std::endl; 
     // and even rethrow again, with backtrace: 
     RETHROW_WITH_BACKTRACE(e); 
    }; 
    } catch(std::runtime_error& e) { 
    std::cout << "Finally, got this exception:\n" 
       << e.what() << std::endl; 
    }; 
}; 

輸出如下:

First caught this exception: 
From 'logged_except.cpp' at line 50: 
This is an example! 
Got this exception: 
From 'logged_except.cpp' at line 53: 
From 'logged_except.cpp' at line 50: 
This is an example! 
Finally, got this exception: 
From 'logged_except.cpp' at line 59: 
From 'logged_except.cpp' at line 53: 
From 'logged_except.cpp' at line 50: 
This is an example! 

與此解決方案的另一個好處是,你可以通過簡單地禁用回溯有條件地定義宏取決於你是否想要追溯(例如調試或發佈構建)。

上面的例子需要C++ 11的特性,但是您可能會想出一個沒有這些特性的等效解決方案(即可變參數模板,decltype,類型特徵等)。

而且您還可以使用C++ 11異常指針使此方案更加方便。

0

是的,你可以通過重寫此功能覆蓋下GCC '扔':

的extern 「C」 無效__cxa_throw(void *的thrown_exception,無效* pvtinfo,無效(* DEST)(無效*))

做完你的東西之後,你應該調用真正的'throw',因爲你應該在Unix下使用'dlsym()'來得到它的地址或者在'mingw'下面使用函數環繞傳遞-WL,-wrap,symbole到鏈接器,就像在這裏GNU gcc/ld - wrapping a call to symbol with caller and callee defined in the same object file一樣,我使用了兩種方法

相關問題