Здравствуйте, csharper, Вы писали:
C>Вот захотелось сделать автоматическое логирование исключений. Есть базовый класс исключения, все от него наследуются. Но тут не совсем понятно когда вызвать метод log() чтобы записать все параметры. Функцию сделать виртуальной нельзя, потому что в конструкторе виртуальные функции не работают. Передавать дополнительные параметры в конструктор (параметры логирования) по иерархии до базового класса тоже не очень красиво. Есть какие-то идеи?
Кидай исключение не голой инструкцией throw YourClass, а обёрткой:
template<class Ex>
enable_if< is_base_and_derived<YourExceptionBase,Ex>, void > // чтобы пресечь кидание неправильных исключений
do_throw(const Ex& ex
, const char* file, int line // приятный бонус: можно узнать, откуда кинуто
)
{
do_log(ex,file,line);
throw ex;
}
#define DO_THROW(ex) do_throw((ex),__FILE__,__LINE__)
Здравствуйте, csharper, Вы писали:
C>Hi
C>Вот захотелось сделать автоматическое логирование исключений. Есть базовый класс исключения, все от него наследуются. Но тут не совсем понятно когда вызвать метод log() чтобы записать все параметры. Функцию сделать виртуальной нельзя, потому что в конструкторе виртуальные функции не работают. Передавать дополнительные параметры в конструктор (параметры логирования) по иерархии до базового класса тоже не очень красиво. Есть какие-то идеи?
C>Спасибо!
Раз конструктор не подходит, можно попробовать воспользоватся деструктором...
Вот захотелось сделать автоматическое логирование исключений. Есть базовый класс исключения, все от него наследуются. Но тут не совсем понятно когда вызвать метод log() чтобы записать все параметры. Функцию сделать виртуальной нельзя, потому что в конструкторе виртуальные функции не работают. Передавать дополнительные параметры в конструктор (параметры логирования) по иерархии до базового класса тоже не очень красиво. Есть какие-то идеи?
Здравствуйте, csharper, Вы писали:
C>Hi
C>Вот захотелось сделать автоматическое логирование исключений. Есть базовый класс исключения, все от него наследуются. Но тут не совсем понятно когда вызвать метод log() чтобы записать все параметры. Функцию сделать виртуальной нельзя, потому что в конструкторе виртуальные функции не работают. Передавать дополнительные параметры в конструктор (параметры логирования) по иерархии до базового класса тоже не очень красиво. Есть какие-то идеи?
Например так...
/**
@ingroup Support
@brief Базовый класс исключения.
*/
//=====================================================================================//
// class Exception //
//=====================================================================================//class Exception : public std::exception
{
private:
std::string m_msg; // строка, с сообщением об ошибкеpublic:
Exception();
Exception(const char* msg);
Exception(const std::string& msg);
Exception(const exception& another);
virtual ~Exception();
public:
/// получить строку с сообщением об ошибкеvirtual const char* what() const;
private:
/// выводит сообщение об ошибке в отладочный потокvoid writeDebugOut() const;
};
inline Exception::Exception() : m_msg("Unknown error.")
{
writeDebugOut();
}
inline Exception::Exception(const char* msg) : m_msg(msg)
{
writeDebugOut();
}
inline Exception::Exception(const std::string& msg) : m_msg(msg)
{
writeDebugOut();
}
inline Exception::Exception(const std::exception& another) : m_msg(another.what())
{
}
inline Exception::~Exception()
{
}
// получить строку с сообщением об ошибкеinline const char* Exception::what() const
{
return m_msg.c_str();
}
// выводит сообщение об ошибке в отладочный потокinline void Exception::writeDebugOut() const
{
DEBUG_OSTREAM("Exception [" << m_msg << "]\n");
}
/**
@ingroup Support
@def MAKE_EXCEPTION(Name,Parent)
Сгенерировать новый тип исключения.
*/
//=====================================================================================//
// #define MAKE_EXCEPTION() //
//=====================================================================================//#define MAKE_EXCEPTION(Name,Parent) \
class Name : public Parent \
{ \
public: \
Name() {} \
Name(const char* msg) : Parent(msg) {} \
Name(const std::string& msg) : Parent(msg) {} \
Name(const Name& another) : Parent(another) {} \
}
Использование:
MAKE_EXCEPTION(FileSystemFailure,Exception);
MAKE_EXCEPTION(FileSystemOpenFileFailure,FileSystemFailure);
// где-то в теле функции
...
throw FileSystemOpenFileFailure("Не удалось открыть файл ["+name+"]");
...
... макрос DEBUG_OSTREAM выводит сообщение об исключении в отладочный поток (окно Output под MSVS).
Здравствуйте, csharper, Вы писали:
C>Hi
C>Вот захотелось сделать автоматическое логирование исключений. Есть базовый класс исключения, все от него наследуются. Но тут не совсем понятно когда вызвать метод log() чтобы записать все параметры. Функцию сделать виртуальной нельзя, потому что в конструкторе виртуальные функции не работают. Передавать дополнительные параметры в конструктор (параметры логирования) по иерархии до базового класса тоже не очень красиво. Есть какие-то идеи?
Здравствуйте, csharper, Вы писали:
C>Hi
C>Вот захотелось сделать автоматическое логирование исключений. Есть базовый класс исключения, все от него наследуются. Но тут не совсем понятно когда вызвать метод log() чтобы записать все параметры. Функцию сделать виртуальной нельзя, потому что в конструкторе виртуальные функции не работают. Передавать дополнительные параметры в конструктор (параметры логирования) по иерархии до базового класса тоже не очень красиво. Есть какие-то идеи?
C>Спасибо!
Самому интересно чем это грозит:
class base_exc
{
public:
virtual ~base_exc(void) = 0 {};
};
template<typename Derived>
class basic_exc :
public base_exc
{
public:
basic_exc()
{
std::cout << typeid(Derived).name() << std::endl;
}
};
class my_exc1 :
public basic_exc<my_exc1>
{
};
Если не поможет, будем действовать током... 600 Вольт (C)
только параметре исключения тебе всегда придется в дополнительные скобки заключать.
Не нравятся макросы, можно какую-нибудь шаблонную функцию придумать.
Но по мне, код для логирования должен быть отделен от кода по порождению исключений.
Кстати, вводя собственный корень для иерархии исключений, ты лишаешься возможности сделать часть исключений производными от std::logic_error, а часть -- от std::runtime_error. Хотя в большинстве случаев это и не важно.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Paranoik, Вы писали:
P>Раз конструктор не подходит, можно попробовать воспользоватся деструктором...
Какая разница? На момент вызова деструктора базы всё остальное разрушено, а vfptr настроен на vtbl базы.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Paranoik, Вы писали:
P>>Раз конструктор не подходит, можно попробовать воспользоватся деструктором... К>Какая разница? На момент вызова деструктора базы всё остальное разрушено, а vfptr настроен на vtbl базы.
Можно оставить в базе полностью сформированное сообщение для логгирования...
Здравствуйте, Paranoik, Вы писали:
P>Можно оставить в базе полностью сформированное сообщение для логгирования...
В том месте, где это сообщение "оставляется", и можно записать его в лог.
Здравствуйте, Кодт, Вы писали:
P>>Раз конструктор не подходит, можно попробовать воспользоватся деструктором... К>Какая разница? На момент вызова деструктора базы всё остальное разрушено, а vfptr настроен на vtbl базы.
Например, базовый класс может содержать указатель на динамический созданный объект с описанием исключения. Каждый производный класс формирует этот объект в своем конструкторе, а базовый логирует информацию из него в своем деструкторе.
Только вот мне интересно, что произойдет, если логировать в деструкторе, вот в этом случае:
void f()
{
...
// По идее, запись в лог нужно сделать здесь.throw my_detailed_exception( ... );
...
}
void g()
{
try
{
f();
}
catch( const my_exception_base & x )
{
log << "Shit happens!" << std::endl;
}
}
По идее, в лог сначала попадет сообщение "Shit happens!", а уже затем описание самого исключения (т.к. на момент работы блока catch объект исключения еще жив).
Еще интереснее дела могут идти в многопоточном приложении. Например, выборос исключения в f() снял блокировку (освободился mutex в деструкторе какого-нибудь scope_guard-а, к примеру) с параллельной нити. Она записала что-нибудь в лог. Затем управление опять получила нить с f(), возвратились в g() и записали что-то в лог. Хотя, по замыслу, в лог запись о появлении проблем должна была попасть еще внутри f(), пока параллельная нить была блокрирована.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
On Thu, 27 Oct 2005 13:41:38 +0400, csharper <37239@users.rsdn.ru> wrote:
> Вот захотелось сделать автоматическое логирование исключений. Есть базовый класс исключения, все от него наследуются. Но тут не совсем понятно когда вызвать метод log() чтобы записать все параметры. Функцию сделать виртуальной нельзя, потому что в конструкторе виртуальные функции не работают. Передавать дополнительные параметры в конструктор (параметры логирования) по иерархии до базового класса тоже не очень красиво. Есть какие-то идеи?