使用RAII嵌套异常

bam*_*s53 12 c++ exception raii nested-exceptions c++11

所以在C++中嵌套异常的方法std::nested_exception是:

void foo() {
  try {
    // code that might throw
    std::ifstream file("nonexistent.file");
    file.exceptions(std::ios_base::failbit);
  }

  catch(...) {
    std::throw_with_nested(std::runtime_error("foo failed"));
  }
}
Run Code Online (Sandbox Code Playgroud)

但是这种技术在每个级别使用显式的try/catch块,希望嵌套异常,这至少可以说是丑陋的.

Jon Kalb扩展为"责任获取是初始化"的RAII,是处理异常而不是使用显式try/catch块的更清晰的方法.使用RAII,显式try/catch块主要仅用于最终处理异常,例如,为了向用户显示错误消息.

查看上面的代码,在我看来,输入foo()可以被视为有责任报告任何异常,std::runtime_error("foo failed")并将细节嵌套在嵌套异常中.如果我们可以使用RAII来获得这个责任,那么代码看起来会更清晰:

void foo() {
  Throw_with_nested on_error("foo failed");

  // code that might throw
  std::ifstream file("nonexistent.file");
  file.exceptions(std::ios_base::failbit);
}
Run Code Online (Sandbox Code Playgroud)

有没有办法在这里使用RAII语法来替换显式的try/catch块?


为此,我们需要一种类型,当它的析构函数被调用时,检查析构函数调用是否是由异常引起的,如果是,则嵌套该异常,并抛出新的嵌套异常,以便正常地展开.这可能看起来像:

struct Throw_with_nested {
  const char *msg;

  Throw_with_nested(const char *error_message) : msg(error_message) {}

  ~Throw_with_nested() {
    if (std::uncaught_exception()) {
      std::throw_with_nested(std::runtime_error(msg));
    }
  }
};
Run Code Online (Sandbox Code Playgroud)

但是,std::throw_with_nested()要求"当前处理的异常"处于活动状态,这意味着除了catch块的上下文之外它不起作用.所以我们需要这样的东西:

  ~Throw_with_nested() {
    if (std::uncaught_exception()) {
      try {
        rethrow_uncaught_exception();
      }
      catch(...) {
        std::throw_with_nested(std::runtime_error(msg));
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

不幸的是,就我所知,rethrow_uncaught_excpetion()C++中没有任何定义.

myt*_*gel 3

如果没有在析构函数中捕获(和消耗)未捕获的异常的方法,则无论是否嵌套,都无法在析构函数的上下文中重新抛出异常而不被std::terminate调用(当在上下文中抛出异常时)异常处理)。

std::current_exception(与 结合std::rethrow_exception)只会返回指向当前处理的异常的指针。这阻止了它在这种情况下的使用,因为这种情况下的异常是明确未处理的。

鉴于上述情况,唯一给出的答案是从美学角度。函数级别的 try 块使这个看起来稍微不那么难看。(根据您的风格偏好进行调整):

void foo() try {
  // code that might throw
  std::ifstream file("nonexistent.file");
  file.exceptions(std::ios_base::failbit);
}
catch(...) {
  std::throw_with_nested(std::runtime_error("foo failed"));
}
Run Code Online (Sandbox Code Playgroud)