如何寫一個完善的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 : publicBaseException
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