【问题标题】:How does `std::terminate` know to treat `std::exception`s specially?`std::terminate` 如何知道要特别对待`std::exception`?
【发布时间】:2015-12-13 19:32:35
【问题描述】:

这个

#include <stdexcept>
struct A /*: public std::exception*/ {
  const char* what() const noexcept { return "this is A";  } 
};
int main(){
  throw A{};
  return 0;
}

给我(stderr):

terminate called after throwing an instance of 'A'
Aborted (core dumped)

如果我取消评论,死亡信息变成:

terminate called after throwing an instance of 'A'
what():  this is A
Aborted (core dumped)

std::terminate怎么知道要特别对待std::exceptions?

如何在我自己的 set_terminate 中进行模拟?我试过了

//...
int main(){
  std::set_terminate([](){
      printf("exception thrown\n");
      std::exception_ptr eptr = std::current_exception();
      std::exception* ptr =  dynamic_cast<std::exception*>(eptr);
      if (ptr) 
        puts(ptr->what());
  });
  throw A{};
}

但由于dynamic_cast 行,它不会编译。

【问题讨论】:

  • @user4815162342 有趣的是,即使我使用-fno-rtti 编译,我也会得到what(): 行。
  • @PSkocik 在链接的文章中指出,即使在禁用 RTTI 的情况下,也使用 RTTI 的某些部分来使异常正常工作。通常这样的事情是使用编译器魔法完成的:内置和编译器特定的代码。 std::terminate 在运行时库的深处实现。 libstdc++ 只是抛出当前异常并尝试捕获std::exception。源代码:searchcode.com/codesearch/view/52744421/#l-47
  • @PSkocik 是的,链接的文章明确指出了这一点。根据作者的发现,-fno-rtti 只是像typeid 一样禁用了前端的功能,但是后台的异常还是使用了实现 RTTI 的机制。

标签: c++


【解决方案1】:

很可能是因为它只是尝试将dynamic_cast 转换为std::exception,并在动态转换成功时调用虚拟what() 方法。

【讨论】:

  • 事实证明它无法尝试将dynamic_cast 转换为std::exception,因为dynamic_cast 仅在对象位于同一层次结构中时才有效。如果不是,它甚至不会编译。
  • @PSkocik : dynamic_casting exception_ptr 没有意义,因为它是不透明类型而不是文字指针。
【解决方案2】:

std::terminate 所做的事情可以大致模拟为:

std::set_terminate([](){
  puts("exception thrown"); 
  //^skip the demangling of the typeid that's normaly done
  try { throw; } 
    catch(const std::exception& e){ puts(e.what()); }
    catch(...){}
  //^rethrow and catch
});

感谢revolver-ocelot 为我指明了正确的方向。

【讨论】:

  • 请注意,您可以仅将 std::rethrow_exception(std::current_exception() 替换为 throw。尽管它在 catch 之外看起来不合适,但它甚至可以在 C++98 上运行。
  • 你领先我三四秒。 :) 以前的版本仍然很有趣,因为我了解了 std::rethrow_exceptionstd::current_exception,它们都是 C++11 中的新版本。
  • @user4815162342 在您发表评论前几秒钟这样做了。
  • 你可能还想要一个catch(...)——否则抛出不是std::exception子类的东西可能会导致调用终止处理程序的循环。
  • 从声明的 noexcept 调用 std:unexpected() 中抛出异常,默认情况下只调用 std::terminate()...
猜你喜欢
  • 1970-01-01
  • 2010-12-06
  • 2015-04-17
  • 1970-01-01
  • 1970-01-01
  • 2012-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多