【问题标题】:C++11 exception design. Idioms and best practices changed?C++11 异常设计。习语和最佳实践改变了吗?
【发布时间】:2013-11-17 03:51:16
【问题描述】:

我想知道,自 C++11 以来,添加了在线程之间传递异常并添加了嵌套异常,通常情况下是否会更改用于异常捕获的惯用语。

现在我们有了:

  1. std::rethrow_if_nested
  2. std::rethrow_with_nested
  3. std::rethrow_exception
  4. std::current_exception

嵌套异常应该用于不丢失异常上下文。

所以现在你可以这样做:

void open_file(std::string const & file_name) {
   try {
      std::ifstream file;
      file.exceptions(ios::failbit | ios::badbit);
      file.open(file_name);
   }
   catch (...) {
      std::rethrow_with_nested(std::logic_error("File " + file_name + 
      " could not be open"));
   }
}

如果我没记错的话,你可以像这样得到回溯:

void print_backtrace(std::exception const & e, int depth = 0) {
   std::cerr << std::string(depth, ' ') << e.what() << std::endl;
   try {
      std::rethrow_if_nested(e);
   }
   catch (std::exception const & ex) {
      print_backtrace(ex, ++depth);
   }
}

因此,如果您将print_backtraceopen_file 一起使用,它应该在输出中为您提供std::logic_error + ios_base::failure

我的问题是:

  1. 考虑到我想在不丢失上下文的情况下捕获所有异常,这个习惯用法是在 c++11 中处理异常的“正确”方式吗?
  2. print_backtrace 函数中是否有办法捕获异常,catch (...) 可以完全捕获所有异常?
  3. 我不知道为什么需要std::rethrow_exception,也不知道什么时候需要。

【问题讨论】:

  • 我对 C++11 知之甚少,但确实觉得很奇怪,您必须重新抛出才能获得嵌套异常。我希望他们能够提供一些检查嵌套异常的方法 不会抛出它们。通常异常代表异常流控制,但在print_backtrace函数的上下文中,检查嵌套异常在概念上是正常流控制。
  • 是的,我也觉得奇怪,其实我也不知道具体的设计,我只记得nested_exception是一个mixin类,你的异常是nested_exception的父类。我还看到它有[[noreturn]] void rethrow_nested() constexception_ptr nested_ptr() const noexcept
  • @Kevin of 2013 - 在 C++ 中,异常适用于正常但异常的情况,例如分配数组的内存太少,或者用户输入了错误的文件名。它们旨在允许继续处理。在造成更多伤害之前,应通过尽可能优雅地终止程序来处理异常情况。
  • @JiveDadson 你对 abnormal 的定义比我的要强一些...我可能会更好地说 unusual vs 通常
  • @Kevin - 我说的是我的意思,我的意思是我说的。 :-) 我在“不正常”的意义上使用异常,也就是说,偏离规范、规则或原则。(“弗兰肯斯蒂恩博士不知道,伊戈尔已经获得了一个异常的大脑。”)@987654321 @ 例外我的意思是需要不寻常的关注。 “活的语言”有问题。话被淡化了。有一个相对较新的例外用法已被编入词典,即作为“不正常”的同义词。呃。我发现保留这种区别很有用。

标签: c++ c++11 exception-handling


【解决方案1】:

1。我不知道我会称之为成语。如果你所说的“正确”是指类似于std::vector 是默认使用的“正确”容器,我认为实际上并没有一种特定的“正确”处理错误的方式。这是一种正确的方法,因为它是明确定义的行为。

2。首先,您必须在不限于某些例外的上下文中调用 print_backtrace(),这意味着您必须在 catch(...) 块中调用它:

    try {
      run();
    } catch(...) {
      print_backtrace();
    }

但是你没有已知类型的异常可以传递给函数。相反,您必须编写函数以不同方式访问异常;通过抛出该异常并在内部捕获它(因为这是您可以将已知类型的变量绑定到任意异常的唯一机制)。

void print_backtrace(int depth = 0) {
  try {
    throw;
  }

  // this block shows how to handle exceptions of some known type
  // You can have your own types instead of std::exception
  catch (const std::exception & e) {
    std::cerr << std::string(depth, ' ') << e.what() << std::endl;
    try {
      std::rethrow_if_nested(e);
    }
    catch (...) {
      print_backtrace(++depth);
    }
  }

  // Not all nesting exceptions will be of a known type, but if they use the
  // mixin type std::nested_exception, then we can at least handle them enough to
  // get the nested exception:

  catch (const std::nested_exception & ne) {
    std::cerr << std::string(depth, ' ') << "Unknown nesting exception\n";

    try {
      ne.rethrow_nested();
    }
    catch (...) {
      print_backtrace(++depth);
    }
  }

  // Exception nesting works through inheritance, which means that if you
  // can't inherit from the type, then you can't 'mixin' std::nesting exception.
  // If you try something like std::throw_with_nested( int{10} ); Then you'll
  // hit this catch block when printing the backtrace.

  catch (...) {
    std::cerr << std::string(depth, ' ') << "Unknown exception\n";
  }
}

3。 std::rethrow_exceptionstd::exception_ptr 一起使用,这是一种可用于传输任意异常的类型。获得该异常的唯一方法是使用正常的异常处理机制来处理catch 异常,这意味着您必须能够抛出该异常。这就是rethrow_exception 所做的。这可用于将任意异常从一个线程传输到另一个线程(如 std::future 所做的那样),或将任意异常作为成员保存(如 std::nested_exception 所做的那样),或将任意异常作为参数传递给将尝试打印一些异常描述的函数。

void print_backtrace(std::exception_ptr e) {
  try {
    std::rethrow_exception(e);
  }

  catch (const std::exception & e) {
    // ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-30
    • 2016-10-02
    • 2018-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-13
    相关资源
    最近更新 更多