如何写一个完善的c++异常处理类
我們的異常處理類的features
如何寫一個異常處理類是一個不太容易的事情,最近剛好接觸了一些不錯的代碼,看到了一些技巧,這里和大家分享一下。
一個相對完善的異常處理類(以及附加的一些東西)應該能夠處理下面的一些功能:
1) 能夠方便的定義異常類的繼承樹
2) 能夠方便的throw、catch,也就是在代碼中捕獲、處理代碼的部分應該更短
3) 能夠獲取異常出現的源文件的名字、方法的名字、行號
4) 能夠獲取異常出現的調用棧并且打印出來
由于目前我用的平臺是linux,所以里面調用的一些函數也只是在linux下面有用。Windows也肯定是具有相應的函數的,具體可能需要去查查
首先科普一些內容:
1) 對于沒有捕獲的異常(no handler),則會終止程序,調用terminate()
2) 在定義函數的時候,我們可以在定義的后面加上throw (exception1, exception2…):
??? a) 如果沒有寫這一段、則可能拋出任意的異常
??? b) 如果寫throw(),則表示函數不能拋出任意的異常
??? c) 如果寫throw(A, B), 表示函數拋出A、B的異常
如果拋出的異常不在列表范圍內,則異常不能被catch,也就會調用terminate()
我們構想一下我們定義、調用我們的異常類的時候是怎樣的一個情形:
1) 定義:
1: class DerivedException : public BaseException 2: { 3: public: 4: MY_DEFINE_EXCEPTION(DerivedException, BaseException); 5: };2) 如何拋出異常
1: MY_THROW(DerivedException)3) 如何catch異常
1: catch (DerivedException& e) 2: { 3: cout<< e.what() << endl; 4: }這個輸出的內容包括錯誤的行號、文件名、方法名、和調用棧的列表
給出我們異常類的頭文件:
1: #ifndef EXCEPTION_TEST 2: #define EXCEPTION_TEST 3:? 4: #include <exception> 5: #include <string> 6:? 7: #define MY_THROW(ExClass, args...) \ 8: do \ 9: { \ 10: ExClass e(args); \ 11: e.Init(__FILE__, __PRETTY_FUNCTION__, __LINE__); \ 12: throw e; \ 13: } \ 14: while (false) 15:? 16: #define MY_DEFINE_EXCEPTION(ExClass, Base) \ 17: ExClass(const std::string& msg = "") throw() \ 18: : Base(msg) \ 19: {} \ 20: \ 21: ~ExClass() throw() {} \ 22: \ 23: /* override */ std::string GetClassName() const \ 24: { \ 25: return #ExClass; \ 26: } 27:? 28: class ExceptionBase : public std::exception 29: { 30: public: 31: ExceptionBase(const std::string& msg = "") throw(); 32:? 33: virtual ~ExceptionBase() throw(); 34:? 35: void Init(const char* file, const char* func, int line); 36:? 37: virtual std::string GetClassName() const; 38:? 39: virtual std::string GetMessage() const; 40:? 41: const char* what() const throw(); 42:? 43: const std::string& ToString() const; 44:? 45: std::string GetStackTrace() const; 46:? 47: protected: 48: std::string mMsg; 49: const char* mFile; 50: const char* mFunc; 51: int mLine; 52:? 53: private: 54: enum { MAX_STACK_TRACE_SIZE = 50 }; 55: void* mStackTrace[MAX_STACK_TRACE_SIZE]; 56: size_t mStackTraceSize; 57: mutable std::string mWhat; 58: }; 59:? 60: class ExceptionDerived : public ExceptionBase 61: { 62: public: 63: MY_DEFINE_EXCEPTION(ExceptionDerived, ExceptionBase); 64: }; 65:? 66: #endif
這個頭文件首先定義了兩個宏,這里先暫時不管他,我先來解釋一下ExceptionBase,它繼承自std::exception,std::exception里面其實已經提供了一些功能了,但是比較弱,為了實現我們上文提到的功能,這里只是繼承了std:exception的借口,也就是what()函數。
上面的接口應該比較好理解,45行的GetStackTrace是打印當前的調用棧,49-51行分別存儲了當前出現exception的源文件名,函數名,行號,54行定義了最大的調用棧顯示的深度,也就是顯示50行。
60行顯示了怎樣定義一個新的異常類,這個就很方便了,通過MY_DEFINE_EXCEPTION宏去定義了一個繼承類,詳情見16行,這里不再細說,我這里想說說7行的MY_THROW宏,使用了3個內置的參數,__FILE__, __LINE__, __PRETTY_FUNCTION__, 他們分別是當前的文件名,行號,和函數名,他們的使用方法是在哪兒出現,其相應的值就是什么。
為什么這里要使用MY_THROW宏呢?其實是為了方便的把行號、文件名等加入進來,宏展開的時候是在一行上的,這樣也使得行號與出錯的行號保持一致,而且讓代碼更簡單。
給出異常類的.cpp文件:
1: #include <execinfo.h> 2: #include <stdlib.h> 3: #include <cxxabi.h> 4:? 5: #include <iostream> 6: #include <sstream> 7:? 8: #include "exception_test.h" 9:? 10: using namespace std; 11:? 12: ExceptionBase::ExceptionBase(const std::string& msg) throw() 13: : mMsg(msg), 14: mFile("<unknown file>"), 15: mFunc("<unknown func>"), 16: mLine(-1), 17: mStackTraceSize(0) 18: {} 19:? 20: ExceptionBase::~ExceptionBase() throw() 21: {} 22:? 23: void ExceptionBase::Init(const char* file, const char* func, int line) 24: { 25: mFile = file; 26: mFunc = func; 27: mLine = line; 28: mStackTraceSize = backtrace(mStackTrace, MAX_STACK_TRACE_SIZE); 29: } 30:? 31: std::string ExceptionBase::GetClassName() const 32: { 33: return "ExceptionBase"; 34: } 35:? 36: const char* ExceptionBase::what() const throw() 37: { 38: return ToString().c_str(); 39: } 40:? 41: const std::string& ExceptionBase::ToString() const 42: { 43: if (mWhat.empty()) 44: { 45: stringstream sstr(""); 46: if (mLine > 0) 47: { 48: sstr << mFile << "(" << mLine << ")"; 49: } 50: sstr << ": " << GetClassName(); 51: if (!GetMessage().empty()) 52: { 53: sstr << ": " << GetMessage(); 54: } 55: sstr << "\nStack Trace:\n"; 56: sstr << GetStackTrace(); 57: mWhat = sstr.str(); 58: } 59: return mWhat; 60: } 61:? 62: std::string ExceptionBase::GetMessage() const 63: { 64: return mMsg; 65: } 66:? 67: std::string ExceptionBase::GetStackTrace() const 68: { 69: if (mStackTraceSize == 0) 70: return "<No stack trace>\n"; 71: char** strings = backtrace_symbols(mStackTrace, 10); 72: if (strings == NULL) // Since this is for debug only thus 73: // non-critical, don't throw an exception. 74: return "<Unknown error: backtrace_symbols returned NULL>\n"; 75:? 76: std::string result; 77: for (size_t i = 0; i < mStackTraceSize; ++i) 78: { 79: std::string mangledName = strings[i]; 80: std::string::size_type begin = mangledName.find('('); 81: std::string::size_type end = mangledName.find('+', begin); 82: if (begin == std::string::npos || end == std::string::npos) 83: { 84: result += mangledName; 85: result += '\n'; 86: continue; 87: } 88: ++begin; 89: int status; 90: char* s = abi::__cxa_demangle(mangledName.substr(begin, end-begin).c_str(), 91: NULL, 0, &status); 92: if (status != 0) 93: { 94: result += mangledName; 95: result += '\n'; 96: continue; 97: } 98: std::string demangledName(s); 99: free(s); 100: // Ignore ExceptionBase::Init so the top frame is the 101: // user's frame where this exception is thrown. 102: // 103: // Can't just ignore frame#0 because the compiler might 104: // inline ExceptionBase::Init. 105: result += mangledName.substr(0, begin); 106: result += demangledName; 107: result += mangledName.substr(end); 108: result += '\n'; 109: } 110: free(strings); 111: return result; 112: } 113:? 114: /* 115: * test-main 116: */ 117: int f2() 118: { 119: MY_THROW(ExceptionDerived, "f2 throw"); 120: } 121: void f1() 122: { 123: try 124: { 125: f2(); 126: } 127: catch (ExceptionDerived& e) 128: { 129: cout << e.what() << endl; 130: } 131: } 132: int main() 133: { 134: f1(); 135: }這是函數的實現代碼,其他的都比較好理解,67行的GetStackTrace是相對復雜一點的,里面用backtrace函數去獲取了當前調用棧的層數,用backtrace_symbols去獲取當前調用棧的符號,而且__cxa_demangle函數的使用也值得去看看,這里不再細說了。
117行后展示了一個測試代碼,代碼雖然定義比較麻煩,不過使用還是很方便的:)。
from:?http://www.cnblogs.com/LeftNotEasy/archive/2010/10/30/1865364.html
總結
以上是生活随笔為你收集整理的如何写一个完善的c++异常处理类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 目前最细致清晰的NSDictionary
- 下一篇: 训练自己haar-like特征分类器并识