在C++中重用异常处理代码

Rod*_*igo 16 c++ exception-handling c++11

我有这两个函数,具有重复的异常处理,其唯一目的是显示错误消息:

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());
  }
}
Run Code Online (Sandbox Code Playgroud)

我可以处理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 Code Online (Sandbox Code Playgroud)
  1. 这是一个好方法吗?
  2. 如果是这样,run_treated将被称为很多.我应该关注性能吗?
  3. 还有其他方法吗?

Sto*_*ica 12

可以选择使用Lippincott函数来集中异常处理逻辑.考虑一下:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 对我来说这看起来并不复杂.非常简单和干净.它具有额外的好处,您不需要通过标头公开异常处理代码(尽管您可以添加另一个层来解决此问题). (6认同)
  • @VittorioRomeo - 一个人的高阶函数是另一个复杂/神秘的人.我不否认这些方法有很大的不同. (5认同)
  • @VittorioRomeo Type-erasing callable意味着你每次都要一直支付类型擦除成本.此处的成本仅在特殊代码路径上支付. (2认同)

Vit*_*meo 11

这是一个好方法吗?

是.它可以防止代码重复,并允许您通过传入lambda轻松自定义行为.


如果是这样,run_treated将被称为很多.我应该关注性能吗?

是.std::function不是一个零成本的抽象.您应该使用模板参数来传递lambda而不需要类型擦除.

template <typename F>
void run_treated(F&& func) noexcept {
  try {
    std::forward<F>(func)();
  } catch // ... all the catches go here
}
Run Code Online (Sandbox Code Playgroud)

我将讨论和基准测试各种技术,以便将函数传递给本文中的其他函数:"将函数传递给函数".

如果您不想使用模板传递func,可以使用类似的东西function_ref (建议用于标准化P0792).这里有一个实现:function_ref.cpp.


无关评论:

  • 那些无条件的noexcept指定者看起来很可疑.你能真正保证没有例外可以逃脱这些功能吗?

  • []()->void {}相当于[]{}.