linux下抛出异常怎么写,[原创]如何写一个完善的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) 定义:

class DerivedException : public BaseException

{

public:

MY_DEFINE_EXCEPTION(DerivedException, BaseException);

};

2) 如何抛出异常

MY_THROW(DerivedException)

3) 如何catch异常

catch (DerivedException& e)

{

cout<< e.what() << endl;

}

这个输出的内容包括错误的行号、文件名、方法名、和调用栈的列表

给出我们异常类的头文件:

#ifndef EXCEPTION_TEST

#define EXCEPTION_TEST

#include

#include

#define MY_THROW(ExClass, args...) \

do \

{ \

ExClass e(args); \

e.Init(__FILE__, __PRETTY_FUNCTION__, __LINE__); \

throw e; \

} \

while (false)

#define MY_DEFINE_EXCEPTION(ExClass, Base) \

ExClass(const std::string& msg = "") throw() \

: Base(msg) \

{} \

\

~ExClass() throw() {} \

\

/* override */ std::string GetClassName() const \

{ \

return #ExClass; \

}

class ExceptionBase : public std::exception

{

public:

ExceptionBase(const std::string& msg = "") throw();

virtual ~ExceptionBase() throw();

void Init(const char* file, const char* func, int line);

virtual std::string GetClassName() const;

virtual std::string GetMessage() const;

const char* what() const throw();

const std::string& ToString() const;

std::string GetStackTrace() const;

protected:

std::string mMsg;

const char* mFile;

const char* mFunc;

int mLine;

private:

enum { MAX_STACK_TRACE_SIZE = 50 };

void* mStackTrace[MAX_STACK_TRACE_SIZE];

size_t mStackTraceSize;

mutable std::string mWhat;

};

class ExceptionDerived : public ExceptionBase

{

public:

MY_DEFINE_EXCEPTION(ExceptionDerived, ExceptionBase);

};

#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文件:

#include

#include

#include

#include

#include

#include "exception_test.h"

using namespace std;

ExceptionBase::ExceptionBase(const std::string& msg) throw()

: mMsg(msg),

mFile(""),

mFunc(""),

mLine(-1),

mStackTraceSize(0)

{}

ExceptionBase::~ExceptionBase() throw()

{}

void ExceptionBase::Init(const char* file, const char* func, int line)

{

mFile = file;

mFunc = func;

mLine = line;

mStackTraceSize = backtrace(mStackTrace, MAX_STACK_TRACE_SIZE);

}

std::string ExceptionBase::GetClassName() const

{

return "ExceptionBase";

}

const char* ExceptionBase::what() const throw()

{

return ToString().c_str();

}

const std::string& ExceptionBase::ToString() const

{

if (mWhat.empty())

{

stringstream sstr("");

if (mLine > 0)

{

sstr << mFile << "(" << mLine << ")";

}

sstr << ": " << GetClassName();

if (!GetMessage().empty())

{

sstr << ": " << GetMessage();

}

sstr << "\nStack Trace:\n";

sstr << GetStackTrace();

mWhat = sstr.str();

}

return mWhat;

}

std::string ExceptionBase::GetMessage() const

{

return mMsg;

}

std::string ExceptionBase::GetStackTrace() const

{

if (mStackTraceSize == 0)

return "\n";

char** strings = backtrace_symbols(mStackTrace, 10);

if (strings == NULL) // Since this is for debug only thus

// non-critical, don't throw an exception.

return "\n";

std::string result;

for (size_t i = 0; i < mStackTraceSize; ++i)

{

std::string mangledName = strings[i];

std::string::size_type begin = mangledName.find('(');

std::string::size_type end = mangledName.find('+', begin);

if (begin == std::string::npos || end == std::string::npos)

{

result += mangledName;

result += '\n';

continue;

}

++begin;

int status;

char* s = abi::__cxa_demangle(mangledName.substr(begin, end-begin).c_str(),

NULL, 0, &status);

if (status != 0)

{

result += mangledName;

result += '\n';

continue;

}

std::string demangledName(s);

free(s);

// Ignore ExceptionBase::Init so the top frame is the

// user's frame where this exception is thrown.

//

// Can't just ignore frame#0 because the compiler might

// inline ExceptionBase::Init.

result += mangledName.substr(0, begin);

result += demangledName;

result += mangledName.substr(end);

result += '\n';

}

free(strings);

return result;

}

/*

* test-main

*/

int f2()

{

MY_THROW(ExceptionDerived, "f2 throw");

}

void f1()

{

try

{

f2();

}

catch (ExceptionDerived& e)

{

cout << e.what() << endl;

}

}

int main()

{

f1();  这是函数的实现代码,其他的都比较好理解,67行的GetStackTrace是相对复杂一点的,里面用backtrace函数去获取了当前调用栈的层数,用backtrace_symbols去获取当前调用栈的符号,而且__cxa_demangle函数的使用也值得去看看,这里不再细说了。

117行后展示了一个测试代码,代码虽然定义比较麻烦,不过使用还是很方便的:)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值