std::nested_exception 和多态性 - 这是可以做到的最好的吗?
std::nested_exception and polymorphism - Is this the best that can be done?
当您只想调用 what()
时,std::nested_exceptions
很好,但访问其他异常类型的接口会变得很丑陋。
让我们假设我有两个异常 classes 存储一些附加信息:
/* CODE BLOCK 1 */
class ErrorI : public std::runtime_error {
public:
ErrorI(int a_integer) : std::runtime_error{"ErrorI"}, integer{a_integer} {}
int integer;
};
class ErrorD : public std::runtime_error {
public:
ErrorD(double a_real) : std::runtime_error{"ErrorD"}, real{a_real} {}
double real;
};
没有嵌套异常,我们可以访问try/catch块中的成员变量:
/* CODE BLOCK 2 */
int main()
{
try {
/* do stuff */;
}
catch(const ErrorI& ee){
std::cout << " Value: " << ee.integer << std::endl;
}
catch(const ErrorD& ee){
std::cout << " Value: " << ee.real << std::endl;
}
}
但是如果我们想打开一个 std::nested_exception
,事情就没那么简单了。我们需要定义一个递归调用的函数,它应该是这样的:
/* CODE BLOCK 3 */
void process_exception(const std::exception& e, int level=0) {
try {
std::rethrow_if_nested(e);
}
catch(const std::exception& e) {
process_exception(e, level+1);
}
/* ... process the top-most (latest) exception ... */
}
不幸的是,为了处理最顶层的异常,我们不能在代码块 2 中使用 try/catch 语法:如果我们重新抛出 e,它将被截断为 std::exception,我们将丢失所有附加信息。 编辑:如果使用 std::rethrow_exception 和 std::exception_ptr,则不正确。
所以我们回到 good-ole 动态类型检查的问题,以及它所需要的一切(参见 this 例如)。
从具有所需接口的公共基础 class 派生所有异常。这包括像 Visitor pattern 这样的方法。这很好,但如果异常 classes 是由外部库提供的,那就不好了。
使用dynamic_cast:
/* CODE BLOCK 4 */
if (auto p = dynamic_cast<ErrorI const*>(&e)) {
std::cout << " Value: " << p->integer << std::endl;
}
else if (auto p = dynamic_cast<ErrorD const*>(&e)) {
std::cout << " Value: " << p->real << std::endl;
}
???
我唯一的选择似乎是求助于 2。如果有任何其他建议,我很想听听。
在 std::exception_ptr
的帮助下,您可能会执行以下操作:
void print_exception(const std::exception_ptr& eptr, int level = 0)
{
try
{
std::rethrow_exception(eptr);
}
catch (const ErrorI& e)
{
std::cerr << std::string(level, ' ') << "exception: " << e.what() << ": " << e.integer << std::endl;
}
catch (const ErrorD& e)
{
std::cerr << std::string(level, ' ') << "exception: " << e.what() << ": " << e.real << std::endl;
}
catch (const std::exception& e)
{
std::cerr << std::string(level, ' ') << "exception: " << e.what() << std::endl;
}
}
// 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_rec(const std::exception_ptr& eptr, int level = 0)
{
print_exception(eptr, level);
try {
try {
std::rethrow_exception(eptr);
}
catch (const std::exception& e)
{
std::rethrow_if_nested(e);
}
} catch(const std::exception&) {
print_exception_rec(std::current_exception(), level+1);
} catch(...) {}
}
what()
时,std::nested_exceptions
很好,但访问其他异常类型的接口会变得很丑陋。
让我们假设我有两个异常 classes 存储一些附加信息:
/* CODE BLOCK 1 */
class ErrorI : public std::runtime_error {
public:
ErrorI(int a_integer) : std::runtime_error{"ErrorI"}, integer{a_integer} {}
int integer;
};
class ErrorD : public std::runtime_error {
public:
ErrorD(double a_real) : std::runtime_error{"ErrorD"}, real{a_real} {}
double real;
};
没有嵌套异常,我们可以访问try/catch块中的成员变量:
/* CODE BLOCK 2 */
int main()
{
try {
/* do stuff */;
}
catch(const ErrorI& ee){
std::cout << " Value: " << ee.integer << std::endl;
}
catch(const ErrorD& ee){
std::cout << " Value: " << ee.real << std::endl;
}
}
但是如果我们想打开一个 std::nested_exception
,事情就没那么简单了。我们需要定义一个递归调用的函数,它应该是这样的:
/* CODE BLOCK 3 */
void process_exception(const std::exception& e, int level=0) {
try {
std::rethrow_if_nested(e);
}
catch(const std::exception& e) {
process_exception(e, level+1);
}
/* ... process the top-most (latest) exception ... */
}
不幸的是,为了处理最顶层的异常,我们不能在代码块 2 中使用 try/catch 语法:如果我们重新抛出 e,它将被截断为 std::exception,我们将丢失所有附加信息。 编辑:如果使用 std::rethrow_exception 和 std::exception_ptr,则不正确。
所以我们回到 good-ole 动态类型检查的问题,以及它所需要的一切(参见 this 例如)。
从具有所需接口的公共基础 class 派生所有异常。这包括像 Visitor pattern 这样的方法。这很好,但如果异常 classes 是由外部库提供的,那就不好了。
使用dynamic_cast:
/* CODE BLOCK 4 */ if (auto p = dynamic_cast<ErrorI const*>(&e)) { std::cout << " Value: " << p->integer << std::endl; } else if (auto p = dynamic_cast<ErrorD const*>(&e)) { std::cout << " Value: " << p->real << std::endl; }
???
我唯一的选择似乎是求助于 2。如果有任何其他建议,我很想听听。
在 std::exception_ptr
的帮助下,您可能会执行以下操作:
void print_exception(const std::exception_ptr& eptr, int level = 0)
{
try
{
std::rethrow_exception(eptr);
}
catch (const ErrorI& e)
{
std::cerr << std::string(level, ' ') << "exception: " << e.what() << ": " << e.integer << std::endl;
}
catch (const ErrorD& e)
{
std::cerr << std::string(level, ' ') << "exception: " << e.what() << ": " << e.real << std::endl;
}
catch (const std::exception& e)
{
std::cerr << std::string(level, ' ') << "exception: " << e.what() << std::endl;
}
}
// 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_rec(const std::exception_ptr& eptr, int level = 0)
{
print_exception(eptr, level);
try {
try {
std::rethrow_exception(eptr);
}
catch (const std::exception& e)
{
std::rethrow_if_nested(e);
}
} catch(const std::exception&) {
print_exception_rec(std::current_exception(), level+1);
} catch(...) {}
}