Есть задача взаимодействия С++ кода с С библиотекой. Взаимодействие происходит путем передачи callback-функций в библиотеку. Все callback-функции обернуты во врапперы на С++. Следовательно эти С++ врапперы могу внутри себя кидать исключения, но неизвестно как поведет себя С библиотека при этом.
Исходя из этого придумал такое решение (максимально упрощенно, для ясности)
Есть базовый класс исключений внутри проекта:
class ExceptionBase
: public std::exception
{
public:
explicit ExceptionBase(char const * state) throw() : state_(state) {}
char const * what() const throw() { return state_; }
// Функция клонирования исключения, см. ниже.
virtual ExceptionSaver * clone() const throw() = 0;
virtual ~ExceptionBase() throw() {}
private:
char const * state_;
};
Для реализации функции клонирования есть специальная обертка:
template <typename Derived>
class ExceptionCloner
: public ExceptionBase
{
public:
typedef ExceptionCloner<Derived> base_;
explicit ExceptionCloner(char const * state) throw()
: ExceptionBase(state)
{}
protected:
virtual ExceptionSaver * clone() const throw()
{
// Реализации ExceptionSaver и ConcreteSaver<> см. ниже
return new(std::nothrow) ConcreteSaver<Derived>(this); // нельзя кидать исключения
}
};
Разные классы исключений определяются так:
class TestExc
: public ExceptionCloner<TestExc>
{
public:
TestExc()
: base_("TestExc")
{}
};
Функция clone возвращает указатель на объект, содержащий в себе копию исключения. Классы определены так:
struct ExceptionSaver
{
virtual void upthrow() const = 0;
virtual ~ExceptionSaver() throw() {}
};
template <typename ExcType>
class ConcreteSaver
: public ExceptionSaver
{
public:
ConcreteSaver(std::exception const * cpy) throw()
: except_(static_cast<ExcType const &>(*cpy))
{}
void upthrow() const throw(ExcType)
{
throw except_;
}
private:
ExcType except_;
};
В коде С++ враппера использовать это планируется совместно с менеджером исключений:
class ExceptionManager
{
public:
ExceptionManager()
: error_(false)
{}
void storeExcept(ExceptionSaver * except) throw() // сохраняем копию исключения
{
error_ = true;
saver_.reset(except);
}
bool isThrowable() const throw() // флаг наличия копии исключения
{
return saver_.get() != 0;
}
bool isError() const throw() // флаг наличия ошибки
{
return error_;
}
void upthrow() const // сбрасываем флаг чтобы не бросать дважды
{
error_ = false;
saver_->upthrow();
}
private:
bool error_;
std::auto_ptr<ExceptionSaver> saver_;
};
В вызывающем коде применяется так:
class CallbackWrapper
{
ExceptionManager manager_;
public:
static void C_callback(void * data)
{
CallbackWrapper * me = reinterpret_cast<CallbackWrapper*>(data);
if(me != 0)
{
try
{
me->this_is_Cpp_Callback();
}
catch(ExceptionBase & e)
{
me->manager_.storeExcept(e.clone()); // регистрация ошибки в менеджере
}
catch(...)
{
me->manager_.storeExcept(0); // неизвестная ошибка
}
}
}
bool Run_C_Code()
{
Set_C_Callback_UserData(reinterpret_cast<void*>(this)); // (примерно) для доступа к членам класса
Run_C_Callback(&CallbackWrapper::C_Callback); // (примерно) для запуска кода из библиотеки с использованием callback-функции, исключения недопустимы
if(manager_.isError()) // факт наличия ошибки
{
if(manager_.isThrowable()) // факт наличия исключения
{
manager_.upthrow();
}
return false; // возвращаем false при отсутствии обработчика в виде исключения
}
return true; // все нормально
}
void this_is_Cpp_Callback()
{
// some C++ code with exceptions
throw TestExc();
}
};
В общем-то вопрос такой: какие проблемы у вышеприведенного подхода и можно ли как-то подругому реализовать эту концепцию?
П.С. Сохранять первоначальный тип исключения — обязательно.