我们经常会有这样的需求,异常或错误(又或者内存泄露时)发生时,如何进行快速定位,定位到文件一级、定位到函数一级、乃至定位到异常出现的行号一级。如此高大上的需求,只需要了解C++ preprocessor内置的一些宏定义如__FILE__(文件名),__LINE__(行号),以及boost\current_function.hpp 中的BOOST_CURRENT_FUNCTION(函数名),将这些宏定义以参数的形式传递给一个异常类,再施以一定的封装,便可轻松实现对异常出现位置的捕捉。
#include <exception>
#include <boost\shared_ptr.hpp>
#include <sstream>
using namespace std;
class Error :public exception
{
public:
Error(const string& file, long line,
const string& func, const string& msg);
const char* what() const; // 重写父类的what函数
private:
string format(const string& file, long line,
const string& func, const string& msg);
boost::shared_ptr<string> _msg;
// 操作_msg, 如同操作一个string*
};
Error::Error(const string& file, long line,
const string& func, const string& msg)
{
_msg = boost::shared_ptr<string>(new string(
format(file, line, func, msg)));
}
string format(const string& file, long line,
const string& func, const string& msg)
{
ostringstream oss; // #include <sstream>
oss << func << ":\n";
oss << file << "(" << line << ").\n" << msg;
return oss.str();
}
const char* Error::what() const
{
return _msg.c_str();
}
客户端程序:
double divide(double x, double y)
{
if (y == 0.)
throw Error(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, "除数不能为0"); // #include <boost\current_function.hpp>
return x/y;
}
int main(int, char**)
{
try
{
divide(1., 0.);
}
catch(exception& e)
{
cout << e.what() << endl;
}
}
C++预处理器也提供了如下的宏定义:
__DATE__
__TIME__
当然一种更常规的做法,利用宏定义(也只能用宏,而不可使用inline 内联函数取代,不由分说的原样替换虽然臭名昭著,却也有时非它不可)的原样替换的特性,对此做进一步的封装,避免显式传参的动作:
#define FAIL(msg)\ std::ostringstream oss; \ oss << msg; \ throw Error(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION,
oss.str());
#define ASSERT(predicate, msg)\ if (!(predicate)) \ { \ FAIL(msg);\ }
这样客户端代码就可改写为:
double divide(double x, double y)
{
if (y == 0.)
{
FAIL("除数不能为0");
// 这一点要尤其注意,一定要将FAIL放在if判断的括号内部
// 如果不这样做的话,if 默认后面的一条语句作为你if判断成立时,要执行的动作
// 这样FAIL宏原样替换的话,就无法识别oss << msg; 中的oss了
}
return x / y;
}
因为FAIL
宏执行的动作是throw
抛异常,如果不对之进行捕获的话,将由编译器进行捕获:
int main(int, char**)
{
double x = 1., y = 0.;
divide(x, y);
return 0;
}
编译器将弹出如下窗口:
int main(int, char**)
{
try
{
double x = 1., y = 0.;
divide(x, y);
}
catch(exception& e)
{
cout << e.what() << endl;
}
return 0;
}
或者我们使用断言的方式:
int main(int, char**)
{
try
{
double x = 1., y = 0.;
ASSERT(y != 0., "除数不能为0");
}
catch(exception& e)
{
cout << e.what() << endl;
}
return 0;
}