Re[10]: delete в Деструкторе
От: Юрий Жмеренецкий ICQ 380412032
Дата: 20.08.09 15:03
Оценка:
Здравствуйте, Николай Ивченков, Вы писали:

НИ>Юрий Жмеренецкий:


ЮЖ>>Может быть передан == стратегия известна.


НИ>Общая стратегия — да, известна. Конкретные же действия, которые нужно выполнить при обработке исключительной ситуации, могут задаваться вызывающим кодом (т.е. они не будет жёстко зашиты в деструктор).


Вот, потихоньку приходим к отвязыванию конкретных действий от состояния. С высокой долей вероятности может возникнуть необходимость использования разных стратегий при выполнении одной и той же операции и придется делать что-то вроде такого:

/*virtual*/ void X::set_error_handler(function<void()>)


ЮЖ>>С TLS еще больше вопросов.


НИ>В случае с TLS стратегию обработки ошибок можно вообще не хранить в объекте, а доставать прямо из хранилища, выделенного для данного потока. Помещать стратегию в конец хранилища и удалять её оттуда будет одна из вызывающих функций.


И в результате получится вручную поддерживаемый стек активных обработчиков. Такая схема может быть жизнеспособной в некоторых специфических случаях, но она никак не выглядит решением для обработки исключений именно в деструкторах (хотя решений как таковых здесь нет, см. ниже).

НИ>В случае принятия решения о размещении небессбойного кода в деструкторе выбор невелик.

НИ>Хотя веских причин для принятия такого решения я не знаю

Я в первую очередь говорю о случаях, когда можно исключить необходимость обработки сбойных случаев в деструкторе. Звучит противоречиво, объясню:
Необходимо наличие нескольких стратегий обработки, которые можно разделить как минимум на два класса эквивалентности: В первом клиент готов обработать исключение и у него есть своя стратегия обработки. Отсутствие такой обработки рассматривается как ошибка в программе. Основная задача инициировать вызов, и в случае ошибки — предпринять какие-либо действия для выхода из состояния, в которое переходит программа после вызова некоторой функции(ий) и которое классифицируется клиентом как ошибочное. Второй класс — игнорирование результата с одновременной гарантией вызова. В этом случае, по классификации клиента, любой сбой не является ошибкой.

Приблизительно вот такой код можно использовать в обоих озвученных стратегиях:

void X::close()
{
  assert(!closed);
  if(!api::close(...))
    throw some_exception();
  assert(closed);
}

X::~X()
{
  if(!closed)
    api::close(...);
}


Здесь возможная проблема — забытый вызов close для первой стратегии. Но, во-первых "отсутствие такой обработки рассматривается как ошибка в программе", во-вторых — при наличии сформулированной задачи, это не проблема(имхо).

Обработка ошибок в деструкторе здесь не нужна по определению (двух вышеописанных стратегий). Если api::close кидает исключения — их просто необходимо игнорировать. Так же как и игнорировать любые возвращаемые результаты.

ЮЖ>>Заодно придется определить семантику хранения членов контекста. Это особенно актуально, если стратегии можно изменять/сохранять.


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


Ссылок может и достаточно, но семантику определить все же придется, иначе ссылки могут быть мертвыми.

ЮЖ>>Все что ты описываешь, можно с легкостью использовать при выносе в отдельный метод с формированием явной инфраструктуры.


НИ>Скорее всего, можно, но на 100% я не уверен (если я правильно понял, имеется в виду удаление всего небессбойного кода из деструктора и размещение его в специальном методе).


В Boost.IOStreams есть концепции closable/flushable — это почти то, о чем я говорю.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.