Try {} catch (...) {} в обратном вызове C - плохая идея?
Я реализую обратные вызовы в С++, которые будут вызываться из обычного кода C. Моя функция main() уже есть С++, но код 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-обратные вызовы?
Ответы
Ответ 1
Да, вы должны поймать исключения и, надеюсь, перевести их во что-то полезное. Предоставление исключений через C-код приводит к поведению undefined. В лучшем случае вы не можете ожидать, что код C сохранит согласованное состояние программы.
См. этот ответ для легкого примера. Более сложный пример с некоторым сложным программным обеспечением, таким как SQLite-C-код, будет захватывать некоторые мьютексы и не будет выпускать его, потому что исключение просто "пролетает", а ваша программа теперь тост.
Кроме того, это имеет любую вероятность "работать", если весь код построен против той же среды выполнения С++. Если у вас есть обратный вызов, реализованный в скрипте Visual С++ 9, а остальная часть кода - в Visual С++ 10, или эти части скомпилированы против статических библиотек времени выполнения - теперь у вас есть две разные среды выполнения, а необработанное исключение в обратном вызове вызывает terminate()
вызываемый.
Ответ 2
sharptooth ответил на весь ваш вопрос, и Джеймс Макнеллис написал хороший сообщение в блоге о том, как элегантно избавиться шаблона с использованием современного С++.
Суть его заключается в использовании функции translate_exceptions, например:
int callback(...) {
return translate_exceptions([&]{
// ... your code here
});
}
Переведенный_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;
}