在 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();
}
  1. 这是一个好方法吗?
  2. 如果是这样,run_treated 将被称为 很多。我应该关注性能吗?
  3. 还有其他方法吗?

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

它是如何工作的?当您在 func1func2 中输入处理程序时,会处理一个 "current exception"。 Lippincott 的主体开始一个新的 try..catch 块并重新抛出它。然后它捕获适当的异常并以集中的方式相应地处理它们。

您还应该注意,您的异常处理逻辑并不是真的 noexcept。理论上可能存在您的列表未涵盖的例外情况。在这种情况下,有几个地方可以调用 std::terminate,具体取决于您如何标记事物 noexcept