我在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回调?
答案 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;
}