C++基础::自制异常定位器

我们经常会有这样的需求,异常或错误(又或者内存泄露时)发生时,如何进行快速定位,定位到文件一级、定位到函数一级、乃至定位到异常出现的行号一级。如此高大上的需求,只需要了解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;
}

编译器将弹出如下窗口:


C++基础::自制异常定位器_第1张图片

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;
}

你可能感兴趣的:(C++,File,line,异常定位器)