Использую C библиотеки в программе на C++. Распространенный механизм обработки ошибок (как в libjpeg, libpng, libcurl) - использование setjmp и задание callback функции, которая вызывается в случае ошибки и делает longjmp. Не могу понять, что делать в C++ - longjmp там использовать, насколько я понимаю, нельзя, а исключения брошенные в callback функции, вызываемой через C, не ловятся. Вот примеры кода:/* c исключениями */
class my_oo_wrapper {
static void error_callback();
void load();
}my_oo_wrapper::error_callback() {
throw "there was an error :(";
}my_oo_wrapper::load() {
c_lib_handle h;c_lib_init(h);
c_lib_set_error_callback(h, error_callback);try {
c_lib_do_stuff(h); // тут происходит ошибка, вызывается error_callback
} catch (char* e) {
// но тут мы ничего не словим, исключение сразу попадет на самый верх в terminate()
/* чистка мусора */
}
}/* c setjmp */
class my_oo_wrapper {
static void error_callback();
void load();
}my_oo_wrapper::error_callback(*some_data) {
// я думаю так делать нельзя, потому что ЭТА функция статическая
// или можно?
longjmp(some_data->jmpbuf, 1);
}my_oo_wrapper::load() {
c_lib_handle h;if (setjmp()) {
// сюда попадем вызвав longjmp
/* чистка мусора */
return;
}c_lib_init(h);
c_lib_set_error_callback(h, error_callback);c_lib_do_stuff(h); // тут происходит ошибка, вызывается error_callback
}
Особого смысла втаскивать error_callback внутрь класса my_oo_wrapper нету - статические функции могут лазить только по статическим членам класса. В остальном всё вроде по-пацански.Единственный вопрос - как именно ты надеешься сделать "чистку мусора". Вариантов там, мягко говоря, до хрена.
>Особого смысла втаскивать error_callback внутрь класса my_oo_wrapper нету - статические функции могут
>лазить только по статическим членам класса. В остальном всё вроде по-пацански.
>Единственный вопрос - как именно ты надеешься сделать "чистку мусора". Вариантов там,
>мягко говоря, до хрена.
Так в том-то и проблема в том что оба способа не работают. Мне, грубо говоря, после вызова error_callback нужно попасть обратно в my_oo_wrapper::load, 2 способами что мне пришли в голову сделать это нельзя. Или единственный вариант - полностью вытащить работу с библиотеками из класса?
>Особого смысла втаскивать error_callback внутрь класса my_oo_wrapper нету - статические функции могут
>лазить только по статическим членам класса. В остальном всё вроде по-пацански.
>
>
>Единственный вопрос - как именно ты надеешься сделать "чистку мусора". Вариантов там,
>мягко говоря, до хрена.Кстати, насчет setjmp - я прав в том, что тут его применять нельзя? Мне уже начинает казаться что можно - ведь из ::load() мы еще не вышли - все ее структуры есть в стеке, включая this, и то что делаем мы longjmp из статической функции класса (либо вообще из левой функции) ничего не значит.
>исключения брошенные в callback функции, вызываемой через C, не ловятсяА можно про это подробнее?
>>исключения брошенные в callback функции, вызываемой через C, не ловятся
>
>А можно про это подробнее?Хех. Проверил. Действительно не ловятся :(
Могу посоветовать только грустное решение с обертками вокруг вызываемых функций. В обертках - setjmp и throw когда результат setjmp не 0, в callback'ах - longjmp.А longjmp'ить куда-то в середину C++ кода действительно выглядит некошерно...
>>>исключения брошенные в callback функции, вызываемой через C, не ловятся
>>
>>А можно про это подробнее?
>
>Хех. Проверил. Действительно не ловятся :(
>Могу посоветовать только грустное решение с обертками вокруг вызываемых функций. В обертках
>- setjmp и throw когда результат setjmp не 0, в callback'ах
>- longjmp.
>
>А longjmp'ить куда-то в середину C++ кода действительно выглядит некошерно...Да, я тоже склоняюсь к такому решению.
В общем, сделал штуку довольно срашную, но вроде бы работает.class my_oo_wrapper {
static void error_callback();
void load();
}void my_oo_wrapper::error_callback(jmpbuf) {
longjmp(jmpbuf, 1);
}void my_oo_wrapper::load() {
c_lib_handle h = 0;jmp_buf buf;
try {
if (setjmp(buf)) {
throw error;
}c_lib_init(h);
c_lib_set_error_callback(h, error_callback, /* передается в callback */ buf);c_lib_do_stuff(h); // тут происходит ошибка, вызывается error_callback
// уборка мусора после нормальной работы
c_lib_cleanup(h);
} catch(...) {
// уборка мусора после ошибки - причем только тех объектов что были инициализированы
if(h)
c_lib_cleanup(h);throw;
}
}
>В общем, сделал штуку довольно срашную, но вроде бы работает.
> c_lib_do_stuff(h); // тут происходит ошибка, вызывается error_callbackА если этот вызов будет глубже (не в той функции где setjmp), что будет с деструкторами локальных объектов C++? 100% не вызовутся...
>>В общем, сделал штуку довольно срашную, но вроде бы работает.
>> c_lib_do_stuff(h); // тут происходит ошибка, вызывается error_callback
>
>А если этот вызов будет глубже (не в той функции где setjmp),
>что будет с деструкторами локальных объектов C++? 100% не вызовутся...Не понял.
error_callback может быть вызван только из того, что вызывается в try-блоке в ::load после setjmp(). Если мы создаем объекты в try блоке, и выпадаем, то в catch мы их, соответственно, удалим. А внутри c_lib_* только чистый C, она чистит свои потроха при вызове c_lib_cleanup.