вторник, 4 марта 2008 г.

Дизайн класса исключения

В большинстве случаев от класса исключения не требуется больше, чем то что уже есть в классе std::exception, то что я напишу дальше можно и не читать Мне недавно понадобилось привязать к исключению произвольные данные, строку, или число, или адрес из контекста в котором произошло исключение. До этого я пользовался обычным, унаследованным от std::exception классом. Самым приемлемым для меня вариантом оказалось использование вариантных данных. Задавать тип данных шаблонным параметром оказалось не удобно, точнее неудобно оказалось потом обрабатывать такие исключения, так как код, обрабатывающий исключение должен знать данные какого типа содержит исключение. В общем как-то так:
template<typename T>
class my_exception_t : public std::exception
{
    T meta_data_;

....

try
{
    do_something_hazardous();
} catch(???)
{
    обработка исключения
}
в блоке catch можно конечно ловить std::exception, но тогда нельзя получить доступ к метаданным исключения, иначе нужно знать как инстанциируется my_exception_t. Альтернативный вариант - определять тип метаданных в рантайме. Для этого я выбрал boost::any, boost::variant нужно собирать, потом добавлять в каждый проект lib, что очень лень)) Получилось такое:
using boost::any_cast;
using boost::any;

class EFailure : public std::exception
{
    any error_code;
public:
    template <typename Param>
    EFailure(Param ec, const char* m) : std::exception(m), error_code(ec) 
    {
    }
    EFailure(const char* m) : std::exception(m), error_code() 
    {
    }
    EFailure(const EFailure& e) : std::exception(e), error_code(e.error_code)
    {
    }
    virtual any get_error_code() const {return error_code;}
};
Ну а для вывода всего этого в поток пришлось сделать страшное))
namespace 
{
    typedef boost::tuple<int, unsigned int, long, unsigned long, double, float, char, unsigned char, std::string> predefined_types;//типы которые может принимать error_code 
    //форматирует error_code и выводит в поток
    template<typename T>
    struct error_code_provider_t;

    template<typename Head, typename Tail>
    struct error_code_provider_t< boost::tuples::cons<Head, Tail> >
    {        
        void operator() (any& value, std::ostream& os)
        {
            if ( value.type() == typeid(Head) )
            {
                os << any_cast<Head>(value);
            } else {
                error_code_provider_t<Tail> next_provider;
                next_provider (value, os);
            }
        }
    };
    template<>
    struct error_code_provider_t<boost::tuples::null_type>
    {
        void operator() (any& value, std::ostream& os)
        {
            os << "can't read this value";
        }
    };

};

std::ostream& operator << (std::ostream& os, const EFailure& f)
{
    error_code_provider_t< predefined_types::cons > error_code_provider;
    os << f.what();
    if ( ! f.get_error_code().empty() )
    {
        os << " ["; error_code_provider(f.get_error_code(), os); os << "]" << std::endl;
    }
    return os;
}

4 комментария:

Анонимный комментирует...

тут вы случайно не перепутали с boost::variant

typedef boost::tuple

картеж - "грубо" массив разнотипных данных.

Анонимный комментирует...

Может обсудим на форуме ?

Анонимный комментирует...

boost-1.41, gcc-4.4.2
в строке "error_code_provider_t< predefined_types::cons > error_code_provider;"
компилятор ругается с сообщением: "‘cons’ is not a member of ‘predefined_types’"

подскажите, как исправить?

Анонимный комментирует...
Этот комментарий был удален администратором блога.