在 C++ 中重用异常处理代码
Reusing exception handling code in C++
我有这两个函数,有重复的异常处理,其唯一目的是显示错误消息:
void func1() noexcept {
try {
do_task();
do_another_task();
} catch (const std::out_of_range& e) {
show_msg("Out of range error", e.what());
} catch (const std::logic_error& e) {
show_msg("Logic error", e.what());
} catch (const std::system_error& e) {
show_msg("System error", e.what());
} catch (const std::runtime_error& e) {
show_msg("Runtime error", e.what());
} catch (const std::exception& e) {
show_msg("Generic error", e.what());
}
}
void func2() noexcept {
try {
do_something();
do_something_else();
do_even_more();
} catch (const std::out_of_range& e) {
show_msg("Out of range error", e.what());
} catch (const std::logic_error& e) {
show_msg("Logic error", e.what());
} catch (const std::system_error& e) {
show_msg("System error", e.what());
} catch (const std::runtime_error& e) {
show_msg("Runtime error", e.what());
} catch (const std::exception& e) {
show_msg("Generic error", e.what());
}
}
我可以只处理 std::exception
并显示一条通用消息,但我想更具体一些,这就是为什么我要捕获所有可能的异常。
我想重用这个异常处理代码。我想到了这个:
void run_treated(std::function<void()> func) noexcept {
try {
func();
} catch // ... all the catches go here
}
void func1() noexcept {
run_treated([]()->void {
do_task();
do_another_task();
});
}
void func2() noexcept {
do_something();
do_something_else();
do_even_more();
}
- 这是一个好方法吗?
- 如果是这样,
run_treated
将被称为 很多。我应该关注性能吗?
- 还有其他方法吗?
Is this a good approach?
是的。它可以防止代码重复,并允许您通过传入 lambda 轻松自定义行为。
If so, run_treated
will be called a lot. Should I be concerned about performance?
是的。 std::function
不是 零成本抽象。您应该使用 模板参数 来传递 lambda 而无需类型擦除。
template <typename F>
void run_treated(F&& func) noexcept {
try {
std::forward<F>(func)();
} catch // ... all the catches go here
}
我在本文中讨论并基准化了将函数传递给其他函数的各种技术:"passing functions to functions"。
如果你不想使用模板传递func
,你可以使用类似function_ref
(建议标准化P0792). An implementation is available here: function_ref.cpp
。
不相关的评论:
那些无条件的 noexcept
说明符看起来很可疑。你真的能保证没有例外会逃脱这些功能吗?
[]()->void {}
等同于 []{}
.
可以选择使用 Lippincott Function 来集中异常处理逻辑。考虑一下:
void Lippincott () noexcept {
try {
throw;
} catch (const std::out_of_range& e) {
show_msg("Out of range error", e.what());
} catch (const std::logic_error& e) {
show_msg("Logic error", e.what());
} catch (const std::system_error& e) {
show_msg("System error", e.what());
} catch (const std::runtime_error& e) {
show_msg("Runtime error", e.what());
} catch (const std::exception& e) {
show_msg("Generic error", e.what());
}
}
void func1() noexcept {
try {
do_task();
do_another_task();
} catch (...) {
Lippincott();
}
}
void func2() noexcept {
try {
do_something();
do_something_else();
do_even_more();
} catch (...) {
Lippincott();
}
}
它是如何工作的?当您在 func1
或 func2
中输入处理程序时,会处理一个 "current exception"。 Lippincott
的主体开始一个新的 try..catch 块并重新抛出它。然后它捕获适当的异常并以集中的方式相应地处理它们。
您还应该注意,您的异常处理逻辑并不是真的 noexcept
。理论上可能存在您的列表未涵盖的例外情况。在这种情况下,有几个地方可以调用 std::terminate
,具体取决于您如何标记事物 noexcept
我有这两个函数,有重复的异常处理,其唯一目的是显示错误消息:
void func1() noexcept {
try {
do_task();
do_another_task();
} catch (const std::out_of_range& e) {
show_msg("Out of range error", e.what());
} catch (const std::logic_error& e) {
show_msg("Logic error", e.what());
} catch (const std::system_error& e) {
show_msg("System error", e.what());
} catch (const std::runtime_error& e) {
show_msg("Runtime error", e.what());
} catch (const std::exception& e) {
show_msg("Generic error", e.what());
}
}
void func2() noexcept {
try {
do_something();
do_something_else();
do_even_more();
} catch (const std::out_of_range& e) {
show_msg("Out of range error", e.what());
} catch (const std::logic_error& e) {
show_msg("Logic error", e.what());
} catch (const std::system_error& e) {
show_msg("System error", e.what());
} catch (const std::runtime_error& e) {
show_msg("Runtime error", e.what());
} catch (const std::exception& e) {
show_msg("Generic error", e.what());
}
}
我可以只处理 std::exception
并显示一条通用消息,但我想更具体一些,这就是为什么我要捕获所有可能的异常。
我想重用这个异常处理代码。我想到了这个:
void run_treated(std::function<void()> func) noexcept {
try {
func();
} catch // ... all the catches go here
}
void func1() noexcept {
run_treated([]()->void {
do_task();
do_another_task();
});
}
void func2() noexcept {
do_something();
do_something_else();
do_even_more();
}
- 这是一个好方法吗?
- 如果是这样,
run_treated
将被称为 很多。我应该关注性能吗? - 还有其他方法吗?
Is this a good approach?
是的。它可以防止代码重复,并允许您通过传入 lambda 轻松自定义行为。
If so,
run_treated
will be called a lot. Should I be concerned about performance?
是的。 std::function
不是 零成本抽象。您应该使用 模板参数 来传递 lambda 而无需类型擦除。
template <typename F>
void run_treated(F&& func) noexcept {
try {
std::forward<F>(func)();
} catch // ... all the catches go here
}
我在本文中讨论并基准化了将函数传递给其他函数的各种技术:"passing functions to functions"。
如果你不想使用模板传递func
,你可以使用类似function_ref
(建议标准化P0792). An implementation is available here: function_ref.cpp
。
不相关的评论:
那些无条件的
noexcept
说明符看起来很可疑。你真的能保证没有例外会逃脱这些功能吗?[]()->void {}
等同于[]{}
.
可以选择使用 Lippincott Function 来集中异常处理逻辑。考虑一下:
void Lippincott () noexcept {
try {
throw;
} catch (const std::out_of_range& e) {
show_msg("Out of range error", e.what());
} catch (const std::logic_error& e) {
show_msg("Logic error", e.what());
} catch (const std::system_error& e) {
show_msg("System error", e.what());
} catch (const std::runtime_error& e) {
show_msg("Runtime error", e.what());
} catch (const std::exception& e) {
show_msg("Generic error", e.what());
}
}
void func1() noexcept {
try {
do_task();
do_another_task();
} catch (...) {
Lippincott();
}
}
void func2() noexcept {
try {
do_something();
do_something_else();
do_even_more();
} catch (...) {
Lippincott();
}
}
它是如何工作的?当您在 func1
或 func2
中输入处理程序时,会处理一个 "current exception"。 Lippincott
的主体开始一个新的 try..catch 块并重新抛出它。然后它捕获适当的异常并以集中的方式相应地处理它们。
您还应该注意,您的异常处理逻辑并不是真的 noexcept
。理论上可能存在您的列表未涵盖的例外情况。在这种情况下,有几个地方可以调用 std::terminate
,具体取决于您如何标记事物 noexcept