在 C 回调中尝试{ } catch(...) { } - 坏主意?
try{ } catch(...) { } in C callback - bad idea?
我正在用 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 回调?
是的,您必须捕获异常并希望将它们转化为有用的东西。让异常通过 C 代码传播会导致未定义的行为。充其量您不能期望 C 代码保持一致的程序状态。
有关简单示例,请参阅 this answer。一个更难的例子是一些复杂的软件,如 SQLite - C 代码将获取一些互斥体并且不会释放它,因为异常只是 "flies through" 并且你的程序现在是 toast.
此外,如果所有代码都是针对相同的 C++ 运行时构建的,这也有可能 "working"。如果你碰巧在 Visual C++ 9 中实现了回调,而 Visual C++ 10 中的其余代码或这些部分是针对静态运行时库编译的——你现在有两个不同的运行时,回调中未处理的异常导致 terminate()
被调用。
sharptooth 回答了你的大部分问题,James McNellis 写了一篇很好的 blog post 关于如何使用现代 C++ 优雅地摆脱样板文件。
它的要点是有一个像这样使用的 translate_exceptions 函数:
int callback(...) {
return translate_exceptions([&]{
// ... your code here
});
}
translate_exception 是一个简单的函数模板(使用函数级别的 try 块)接受一个可调用对象:
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;
}
我正在用 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 回调?
是的,您必须捕获异常并希望将它们转化为有用的东西。让异常通过 C 代码传播会导致未定义的行为。充其量您不能期望 C 代码保持一致的程序状态。
有关简单示例,请参阅 this answer。一个更难的例子是一些复杂的软件,如 SQLite - C 代码将获取一些互斥体并且不会释放它,因为异常只是 "flies through" 并且你的程序现在是 toast.
此外,如果所有代码都是针对相同的 C++ 运行时构建的,这也有可能 "working"。如果你碰巧在 Visual C++ 9 中实现了回调,而 Visual C++ 10 中的其余代码或这些部分是针对静态运行时库编译的——你现在有两个不同的运行时,回调中未处理的异常导致 terminate()
被调用。
sharptooth 回答了你的大部分问题,James McNellis 写了一篇很好的 blog post 关于如何使用现代 C++ 优雅地摆脱样板文件。
它的要点是有一个像这样使用的 translate_exceptions 函数:
int callback(...) {
return translate_exceptions([&]{
// ... your code here
});
}
translate_exception 是一个简单的函数模板(使用函数级别的 try 块)接受一个可调用对象:
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;
}