使用std::nested_exception

引言

C++中异常处理机制是C++相比与C的主要特征之一,其中一点有点令人沮丧,就是无法像java那样直接使用printStackTrace来进行栈追踪,其实是可以的,早在C++11中就引入了这么一个强大的机制,即std::nested_exception.,正确的使用可以使我们在C++中实现printStackTrace,可以帮助我们更好的排查错误.这是这个类现在看来唯一的用处,但是却是一个十分强大的武器.

我们来看一个官网给出的小demo

#include <iostream>
#include <stdexcept>
#include <exception>
#include <string>
#include <fstream>
 
// prints the explanatory string of an exception. If the exception is nested,
// recurses to print the explanatory of the exception it holds
void print_exception(const std::exception& e, int level =  0)
{
    std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n';
    try {
        std::rethrow_if_nested(e);
    } catch(const std::exception& e) {
        print_exception(e, level+1);
    } catch(...) {}
}
 
// sample function that catches an exception and wraps it in a nested exception
void open_file(const std::string& s)
{
    try {
        std::ifstream file(s);
        file.exceptions(std::ios_base::failbit);
    } catch(...) {
        std::throw_with_nested( std::runtime_error("Couldn't open " + s) );
    }
}
 
// sample function that catches an exception and wraps it in a nested exception
void run()
{
    try {
        open_file("nonexistent.file");
    } catch(...) {
        std::throw_with_nested( std::runtime_error("run() failed") );
    }
}
 
// runs the sample function above and prints the caught exception
int main()
{
    try {
        run();
    } catch(const std::exception& e) {
        print_exception(e);
    }
}

我们从run()开始看:
先不用管open_file,干了什么,我们只需要知道它抛出了一个异常,然后我们在catch中拿到这个异常,然后执行throw_with_nested,这个函数是什么意思呢,其实就是把其参数加到nested_exception中,有加入肯定会有取出,与之配套的函数就是rethrow_if_nested,这个一会会看到,然后我们进入open_file,文件未打开设置failbit,流将抛出,进入catch.可以看到其向nested_expection加入了一个runtime_error的异常.

再看看print_exception,其实就是一个简单的递归,终止条件就是rethrow_if_nested不抛出,我们来执行下程序看一看

exception: run() failed
 exception: Couldn't open nonexistent.file
  exception: basic_ios::clear: iostream error

一个典型的栈追踪功能就实现了

值得一提的是初始抛出的异常会直接加到nested_exception,如果我们把程序改成这样

void open_file(const std::string& s)
{
    try {
        std::ifstream file(s);
        file.exceptions(std::ios_base::failbit);
    } catch(...) {
        //std::throw_with_nested( std::runtime_error("Couldn't open " + s) );
        throw;
    }
}
 
// sample function that catches an exception and wraps it in a nested exception
void run()
{
    try {
        open_file("nonexistent.file");
    } catch(...) {
        //std::throw_with_nested( std::runtime_error("run() failed") );
        throw;
    }
}

执行结果是这样的,符合预期

exception: basic_ios::clear: iostream error

总结

std::nested_exception意味着我们可以在几乎没有任何开销的情况下精确的找到错误点,不但对于程序的构建没有任何的影响,且不需要大量的日志,这是个值得在我们的代码中大规模使用的一个工具.

参考:
http://www.cplusplus.com/reference/ios/ios/exceptions/
https://en.cppreference.com/w/cpp/error/nested_exception
https://qa.1r1g.com/sf/ask/2605911031/
https://stackoverflow.com/questions/8397200/how-to-use-stdnested-exception-and-friends

posted @ 2022-07-02 13:18  李兆龙的博客  阅读(207)  评论(0编辑  收藏  举报