Отложенный выброс исключений
От: Аноним  
Дата: 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();
    }
};


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

П.С. Сохранять первоначальный тип исключения — обязательно.
Re: Отложенный выброс исключений
От: Кодт Россия  
Дата: 13.08.09 11:17
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Есть задача взаимодействия С++ кода с С библиотекой. Взаимодействие происходит путем передачи callback-функций в библиотеку. Все callback-функции обернуты во врапперы на С++. Следовательно эти С++ врапперы могу внутри себя кидать исключения, но неизвестно как поведет себя С библиотека при этом.



Можно отказаться от классов-обёрток исключений в пользу boost/any-подобного универсального хранилища:
class ExceptionStorage
{
    template<class T> static void do_throw(T v) { throw v; }
    
    typedef boost::function<void()> voidfunc;
    voidfunc m_throw;
public:
    template<class T> T assign(T v)
    {
        m_throw = boost::bind(do_throw<T>, v);
        return v;
    }
    void reset()
    {
        m_throw = voidfunc();
    }
    void fire()
    {
        if(!m_throw) return;
        voidfunc tmp; swap(m_throw, tmp);
        tmp(); // do_throw<???>(???)
    }
    bool assigned() { return !!m_throw; }
};

ExceptionStorage g_ExceptionStorage;

void my_callback()
{
    .....
    // запоминаем и кидаем
    throw g_ExceptionStorage.assign(std::logic_error("whazzup?"));
    .....
}

void my_callback_frontend()
{
    try
    {
        my_callback();
    }
    catch(...)
    {
        assert(g_ExceptionStorage.assigned());
    }
}

void my_invoke_wrapper()
{
    invoke(my_callback_frontend);
    g_ExceptionStorage.fire();
}


Минусы очевидны: даже если исключение не долетает до my_callback_frontend()/catch(...), оно всё равно копируется внутрь хранилища.
А плюс в том, что мы совершенно не привязаны к типу исключения.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re: Отложенный выброс исключений
От: sraider http://dvinogradov.blogspot.com
Дата: 14.08.09 19:05
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Есть задача взаимодействия С++ кода с С библиотекой. Взаимодействие происходит путем передачи callback-функций в библиотеку. Все callback-функции обернуты во врапперы на С++. Следовательно эти С++ врапперы могу внутри себя кидать исключения, но неизвестно как поведет себя С библиотека при этом.


Boost.Exception не подходит под твою задачу?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.