使用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