динамическая обработка исключений
От: s.p.  
Дата: 07.11.05 12:39
Оценка:
Иногда (например при состыковке библиотек) возникает подобный код:
try{
  DoSomething();
}
catch(std::exception &){
  throw MyError();
}
catch(DB::Exception& de){
  throw MyError();
}
...
catch(...){
  throw MyError();
}

ИМХО это громоздко и плохо расширяемо.
Также иногда хотелось бы в библиотеке позволить пользователю выбирать некритические исключения, обрабатываемые "на месте", задаваемые пользователем же обработчиками.
Предлагаю следующий вариант решения таких вопросов
/// генератор кортежей из последовательности типов
template< typename Types > 
struct TupleGen 
    : boost::mpl::reverse_fold<
        Types
        , boost::tuples::null_type
        , boost::tuples::cons<boost::mpl::_2,boost::mpl::_1>
    >
{
};
class AnyException
{
};
/// обработчик исключений
template<
typename ETL,
typename HTL,
typename BT=void(*)()
>
class ExcHandler
{
public:
    typedef BT BodyType;
    typedef HTL HandlersTypeList;
    typedef ETL ExceptionTypeList;
    typedef typename TupleGen<HandlersTypeList>::type HandlersType;
    ExcHandler(const BodyType &abody, const HandlersType &hd)
        :body_(abody), handlers_(hd){            
//            Если вываливаемся тут, значит кол-во типов в массиве
//            ExceptionTypeList не равно кол-ву типов в массиве HandlersTypeList.
            BOOST_MPL_ASSERT_MSG(boost::mpl::size<HandlersTypeList>::type::value==
                boost::mpl::size<ExceptionTypeList>::type::value, 
                HANDLERS_EXCEPIONS_ARRAY_MISTMATCH, (int));
    }
    void operator()(){
        typedef typename boost::mpl::int_<boost::mpl::size<ExceptionTypeList>::value-1> TP;
        this->Exec(TP());
    }
private:
    BodyType body_;
    HandlersType handlers_;
    template<typename T> void Exec(T){
        typedef typename boost::mpl::at_c<ExceptionTypeList, T::value>::type CurrentExType;
        TryCatch<T, CurrentExType>(boost::is_same<CurrentExType, AnyException>());
    };

    void Exec(boost::mpl::int_<-1>::type){
        body_();
    };

    template<typename T, typename Ex> void TryCatch(boost::false_type){
        try{
            Exec(typename boost::mpl::prior<T>::type());
        }
        catch(Ex &ex){
            boost::get<T::value>(handlers_)(ex);
        }
    }
    
    template<typename T, typename Ex> void TryCatch(boost::true_type){
        try{
            Exec(typename boost::mpl::prior<T>::type());
        }
        catch(...){
            AnyException e;
            boost::get<T::value>(handlers_)(e);
        }
    }

Пример использования:

using namespace boost::mpl;
using namespace boost;
// класс исключений
class MyException{
};
// тело try-блока - должно иметь operator()()
void gg()
{
    cout<<"insight try|catch"<<endl;
    throw runtime_error("sdasd");
}
// обработчики исключений
void RE(runtime_error& e){cout<<"r_t handler:"<<e.what()<<endl;}
void E(exception& e){cout<<"e handler:"<<e.what()<<endl;}
void ME(MyException&){cout<<"m_e handler"<<endl;}
void Unk(AnyException &){cout<<"unknown error";}
///использование
void g(){
    // типы перехватываемых исключений, AnyException - для catch(...)
    typedef boost::mpl::vector
    <
        runtime_error, exception, MyException, AnyException
    > exc;
    // типы обработчиков исключений - должны иметь operator()(Exception&), 
    // Exception - перехватываемое исключение
    typedef boost::mpl::vector
    <
          void(*)(runtime_error&)
        , void(*)(exception&)
        , void(*)(MyException&)
        , void(*)(AnyException&)
    > hand;
    // сам обработчик, в качестве параметров передаем тело try-блока и набор обработчиков
    ExcHandler<exc, hand> hd(&gg, make_tuple(&RE, &E, &ME, &Unk));    
    hd();
}

Или вот так:
class Handler{
public:
    void operator()(runtime_error& e){cout<<"r_t handler:"<<e.what()<<endl;}
    void operator()(exception& e){cout<<"e handler:"<<e.what()<<endl;}
    void operator()(MyException&){cout<<"m_e handler"<<endl;}
    void operator()(AnyException &){cout<<"unknown error";}
    void operator()(){
        cout<<"insight try|catch"<<endl;
        throw MyException();
    }
};
....
typedef boost::mpl::vector
<
    runtime_error, exception, MyException, AnyException
> exc;
typedef boost::mpl::vector
    <
    Handler,
    Handler,
    Handler,
    Handler
    > hand;
Handler h;
ExcHandler<exc, hand, Handler>  hd(h, make_tuple(h, h, h, h));    
hd();

Поведение отличается от стандартного в случае если при обработке исключения выбрасывается исключение типа обрабатываемого ниже, то оно будет обработано ниже(т.е. если в предыдущем примере в обработчике runtime_error выкинуть например logic_error, то последне будет перехвачено обработчиком exception)
Проверялось на MSVC 7.1, gcc 4.0.0, boost 1.33.0.
Жду критики
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.