Будут ли исключения С++ безопасно распространяться через C-код?
У меня есть приложение на С++, которое вызывает SQLite (SQLite находится в C) sqlite3_exec(), который, в свою очередь, может вызвать функцию обратного вызова, реализованную на С++. SQLite скомпилирован в статическую библиотеку.
Если исключение ускорит мой обратный вызов, он будет безопасно распространяться через код C SQLite на код С++, вызывающий sqlite3_exec()?
Ответы
Ответ 1
Я предполагаю, что это зависит от компилятора. Однако бросать исключение в обратный вызов было бы очень плохой идеей. Либо он будет плоский, либо не работает, либо код C в библиотеке SQLite не сможет его обработать. Рассмотрим, является ли это некоторым кодом в SQLite:
{
char * p = malloc( 1000 );
...
call_the_callback(); // might throw an exception
...
free( p );
}
Если исключение "работает", код C не имеет возможности поймать его, и p никогда не будет освобожден. То же самое касается любых других ресурсов, которые библиотека, возможно, выделила, конечно.
Ответ 2
Уже существует протокол для обратного вызова, чтобы прервать вызов API. Из документы:
Если возвращается обратный вызов sqlite3_exec() отличная от нуля, процедура sqlite3_exec() возвращает SQLITE_ABORT без вызова обратный вызов снова и без запуска любые последующие операторы SQL.
Я настоятельно рекомендую вам использовать это вместо исключения.
Ответ 3
SQLite ожидает, что вы вернете ошибку SQLITE_ABORT и код возврата 0 без ошибок. Таким образом, вы должны обернуть весь ваш обратный вызов С++ в try catch. Затем в catch вы получите код ошибки SQLite SQLITE_ABORT, в противном случае - ноль.
Проблемы будут возникать, если вы обходите возврат через SQLite, поскольку он не освободит/не завершит какой бы то ни было код после возврата из вашего обратного вызова. Это вызовет неисчислимые проблемы, потенциально некоторые из которых могут быть очень неясными.
Ответ 4
Если ваш callback, вызванный из sqlite, из того же потока, из которого вы вызвали sqlite3_exec(), бросок где-то в стоп-кассе должен быть пойман путем улова более высокого уровня.
Тестирование этого себя должно быть простым, нет?
[править]
После копания немного больше я узнал, что стандарт С++ несколько неопределенен в отношении того, какое поведение должна вызывать функция С++, вызванная из c, при метании исключения.
Вы должны обязательно использовать механизм обработки ошибок, ожидаемый API. В противном случае вы будете главным образом самим API в состоянии undefined, и любые дальнейшие вызовы могут потенциально сбой/сбой.
Ответ 5
Это был действительно интересный вопрос, и я испытал это из любопытства. В моей ОС X w/gcc 4.2.1 ответ был ДА. Он работает отлично. Я думаю, что настоящий тест будет использовать gcc для С++ и некоторых других (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;
}