您的位置:首页 > 编程语言 > C语言/C++

如何写一个完善的c++异常处理类

2016-05-22 17:30 447 查看


我们的异常处理类的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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: