我有一个调用SQLite的C ++应用程序(SQLite在C中)sqlite3_exec(),后者可以调用我在C ++中实现的回调函数。 SQLite被编译成静态库。
如果异常转义我的回调,它会通过SQLite的C代码安全地传播到调用sqlite3_exec()的C ++代码吗?
答案 0 :(得分:29)
我的猜测是这取决于编译器。但是,在回调中抛出异常将是一个非常糟糕的主意。它要么完全不起作用,要么SQLite库中的C代码将无法处理它。考虑这是否是SQLite中的一些代码:
{
char * p = malloc( 1000 );
...
call_the_callback(); // might throw an exception
...
free( p );
}
如果异常“有效”,则C代码无法捕获它,并且永远不会释放p。当然,库可能已分配的任何其他资源也是如此。
答案 1 :(得分:14)
已经有一个回调协议可以中止API调用。来自the docs:
如果sqlite3_exec()回调返回 非零,sqlite3_exec()例程 返回SQLITE_ABORT而不调用 再次回调而不运行 任何后续的SQL语句。
我强烈建议您使用此而不是例外。
答案 2 :(得分:10)
SQLite希望您在出错时返回SQLITE_ABORT,并返回0返回代码,没有错误。因此,您应该将所有C ++回调包装在try catch 中。然后在catch中返回一个SQLite SQLITE_ABORT错误代码,否则为零。
如果你绕过通过SQLite返回会出现问题,因为它不会释放/完成从你的回调返回后它执行的任何代码。这将导致一些未解决的问题,其中一些问题可能非常模糊。
答案 3 :(得分:1)
这是一个非常有趣的问题,我出于好奇而自行测试。在我的OS X w / gcc 4.2.1上答案是肯定的。它完美地运作。我认为一个真正的测试是使用gcc用于C ++和其他一些(MSVC?,LLVM?)用于C部分,看它是否仍然有用。
我的代码:
callb.h:
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*t_callb)();
void cfun(t_callb fn);
#ifdef __cplusplus
}
#endif
callb.c:
#include "callb.h"
void cfun(t_callb fn) {
fn();
}
main.cpp中:
#include <iostream>
#include <string>
#include "callb.h"
void myfn() {
std::string s( "My Callb Except" );
throw s;
}
int main() {
try {
cfun(myfn);
}
catch(std::string s) {
std::cout << "Caught: " << s << std::endl;
}
return 0;
}
答案 4 :(得分:0)
如果你从sqlite调用的回调来自你调用sqlite3_exec()的同一个线程,那么在callstack中的某个地方应该被更高级别的catch捕获。
自己进行测试应该是直截了当的,不是吗?
[编辑] 在自己挖掘了一点之后,我发现C ++标准对于c语言函数在抛出异常时应该具有的行为有些模糊。
您绝对应该使用API期望的错误处理机制。否则,您主要是API本身处于未定义状态,任何进一步的调用都可能失败/崩溃。