在C回调中尝试{} catch(...){} - 糟糕的主意?

时间:2015-02-24 08:56:30

标签: c++ exception-handling callback

我在C ++中实现了回调,它将从普通的C代码中调用。我的main()函数已经是C ++,但C代码将负责创建最终调用我的回调的线程。

现在我的回调看起来像是

int handle_foo(void *userdata) {
    try {
        MyCPPClass *obj = static_cast<MyCPPClass *>(userdata);
        obj->doStuff();
        return 0; // no error
    } catch(...) {
        LogError("doStuff failed"); 
        return -1; // error
    }
}

这很好用,但对我来说似乎很奇怪。此外,我失去了一些有用的功能,例如找出抛出了什么的能力(没有向我的每个回调中添加大量额外的catch语句)。

这里try {} catch(...) {}是否合理,还是有更好的方法来编写我的C回调?

2 个答案:

答案 0 :(得分:7)

是的,您必须捕获异常并希望将它们转换为有用的东西。让异常通过C代码传播会导致未定义的行为。充其量,你不能指望C代码保持一致的程序状态。

有关简单示例,请参阅this answer。一个更难的例子是使用一些复杂的软件,例如SQLite - C代码会获取一些互斥量并且不会释放它,因为异常只是“飞过”而你的程序现在是吐司。

如果所有代码都是针对相同的C ++运行时构建的,那么这也有可能“正常工作”。如果您碰巧在Visual C ++ 9中实现了回调,并且其他代码在Visual C ++ 10中编写或者这些部分是针对静态运行时库编译的 - 您现在有两个不同的运行时,并且回调中的未处理异常会导致{{1被叫。

答案 1 :(得分:3)

sharptooth回答了你的大部分问题,James McNellis写了一篇关于如何使用现代C ++优雅摆脱样板文件的blog post

它的要点是使用如下所示的translate_exceptions函数:

int callback(...) {
   return translate_exceptions([&]{
      // ... your code here
   });
}

translate_exception是一个简单的函数模板(使用函数级别的try块)取一个invokable:

template<typename Fn> int translate_exception(Fn fn) try {
  fn();
  return 0;
} catch(std::exception const& e) {
  LOG("[EE] exception ", e.what());
  return -2;
} catch( ... ) {
  LOG("[EE] unknown exception");
  return -1;
}