C 回调中的 try catch(...) - 坏主意?
Posted
技术标签:
【中文标题】C 回调中的 try catch(...) - 坏主意?【英文标题】:try catch(...) in C callback - bad idea?C 回调中的 try catch(...) - 坏主意? 【发布时间】:2015-02-24 08:56:29 【问题描述】:我正在用 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 回调?
【问题讨论】:
如果doStuff()
的事件抛出异常,你的调用C 程序是否真的可以做些什么来继续?如果它无法解决异常,那么拥有异常 IMO 没有任何好处。知道您的handle_foo
永远不会抛出,这将使您的调用代码更易于阅读。
您必须防止任何异常离开从 C 函数调用的函数,并且尝试 catch 是这样做的方法。如果你需要你的异常“穿越”一个 C 层,你可以使用一个全局异常 ptr(并不是说这会是好的风格)。从本质上讲,这为您提供了更强大的 errno 机制,但是当您的 c 代码返回时,您仍然必须手动检查错误。
Furthermore, I lose some useful features such as the ability to find out what was thrown
。捕捉std::exception
并记录exception::what
。不要抛出任何不是从std::exception
派生的东西。
【参考方案1】:
是的,您必须捕获异常并希望将它们转化为有用的东西。让异常通过 C 代码传播会导致未定义的行为。充其量你不能期望 C 代码保持一致的程序状态。
有关简单示例,请参阅this answer。一个更难的例子是使用一些复杂的软件,例如 SQLite - C 代码将获取一些互斥体并且不会释放它,因为异常只是“飞过”并且您的程序现在是 toast。
如果所有代码都是针对同一个 C++ 运行时构建的,这也有可能“工作”。如果你碰巧在 Visual C++ 9 中实现了回调,而在 Visual C++ 10 中实现了其余代码,或者这些部分是针对静态运行时库编译的 - 你现在有两个不同的运行时并且回调中未处理的异常导致 terminate()
被调用。
【讨论】:
@MatsPetersson:谢谢。 感谢您确认我的怀疑。因此,捕获所有异常不仅是一个好主意,而且实际上是必需的。如何确定异常的性质?除了将捕获物分解成不同的情况之外,没有通用的方法吗? @nneonneo 您可以在回调中记录或以其他方式显示异常详细信息。您也可以将详细信息隐藏在某种全局变量中 - 这就是 COMSetErrorInfo()
所做的。
@nneonneo:好的库要么从std::exception
派生它们的异常(直接或间接),要么在它们自己的异常层次结构中拥有一个或最多几个基类......一般来说你可以只需为这些基类提供明确的捕获案例,然后使用它们的虚函数来获取错误的详细信息。如果这变得非常冗长并且必须在代码中的数百个地方重现,请考虑添加一个额外的函数层来映射回合理数量的异常,或者 - 最后的手段 - 依靠宏:-(。
【参考方案2】:
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 回调中的 try catch(...) - 坏主意?的主要内容,如果未能解决你的问题,请参考以下文章
try catch 小结 , node的回调callback里不能捕获异常 , 不能被v8优化(现在能了),
aspx页面使用ajax遇到try catch中使用Response.End()报错