Отложенный выброс исключений
От: Аноним  
Дата: 13.08.09 08:21
Оценка:
Есть задача взаимодействия С++ кода с С библиотекой. Взаимодействие происходит путем передачи 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();
    }
};


В общем-то вопрос такой: какие проблемы у вышеприведенного подхода и можно ли как-то подругому реализовать эту концепцию?

П.С. Сохранять первоначальный тип исключения — обязательно.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.