Обнаружение активных исключений в деструкторе

У меня есть класс, который использует RAII для очистки на случай, если что-то пойдет не так. Это означает, что класс содержит флаг, который сообщает ему, была ли работа завершена, и если этот флаг не установлен при вызове конструктора, он выполняет свои задачи очистки и создает сообщения журнала. Теперь я хотел бы, чтобы этот класс стал на один шаг умнее, т.е. он должен был бы выяснить, произошла ли ошибка из-за того, что работа была прервана (т.е. было выброшено исключение и был вызван деструктор) или потому что кто-то неправильно использовал этот класс и никогда фактически закончил работу. Это означает, что мне нужно будет узнать в деструкторе, активно ли исключение. Если он будет найден, я создам сообщение журнала, возможно, распечатав содержимое исключения, а затем повторно выдав его. Я предполагаю что-то вроде этого.

Foo::~Foo () {
  try { /* do not know what to put here */ }
  catch( const std::exception & ex ) {
     // produce error in log
     throw;
  }
  catch( ... ) {
    // produce some other log message
    throw;
   }
}

Однако я не уверен, что это вообще сработает, поскольку исключение активно уже до вызова деструктора и не происходит из блока try. Также я использую throw; внутри деструктора, и создание исключения в этот момент - действительно плохая идея. Поэтому я бы не стал этого делать, если только стандарт четко не гарантирует, что этот случай является исключением (без каламбура) из этого правила (которого я не знаю).

Так это вообще возможно, или я должен поступить в этой ситуации как-то по-другому?


person LiKao    schedule 01.04.2011    source источник


Ответы (3)


Вы можете использовать std::uncaught_exception(), который возвращает true, если было создано исключение, но catch еще не обработало его. Вы можете использовать эту проверку в своем деструкторе, чтобы принимать решения о том, что он должен или не должен делать.

Предостережение: хорошие рекомендации по программированию обычно предписывают, что ваш деструктор ведет себя значительно по-разному в разных обстоятельствах, как правило, не очень хорошая идея. Так что используйте эту функцию, но не злоупотребляйте ею. Обычно используется деструктор, который генерирует исключение только в том случае, если нет активного необработанного исключения (эта функция возвращает false). Но такой тип поведения, как правило, не является хорошим дизайном. Если условие было достаточно плохим, чтобы гарантировать исключение, его, вероятно, не следует игнорировать. И в любом случае деструкторы не должны генерировать исключения.

Пример:

Foo::~Foo()
{
    if (std::uncaught_exception()) 
    {
        std::cerr << "Warning: Can't cleanup properly" << std::endl;
        return;
    }
    else
    {
        // ...
    }
}
person SoapBox    schedule 01.04.2011
comment
этот пример небезопасен, оператор ‹‹ может выдать ios_base::failure, вам нужна дополнительная попытка/поймать(...) в журнале - person Alessandro Teruzzi; 01.04.2011
comment
Это просто сломано. Что, если при создании Foo уже было исключение? - person Deduplicator; 30.01.2016

Это невозможно сделать безопасным образом.

Что вы можете сделать, так это проверить свои параметры в деструкторе, и это должно сказать, завершена ли обработка.

Кстати, почему вы не регистрируете ошибку в блоке catch, где вы можете ее обработать? Там вы должны знать, что обработка была завершена с ошибкой.

person BЈовић    schedule 01.04.2011
comment
Да, я уже проверяю параметры. Однако то, что делает мой класс, относится к тому типу, когда вам нужно выполнить несколько операций, а затем вы делаете атомарную фиксацию. Однако я хочу проверить, если что-то пошло не так, другой код никогда не дойдет до этапа фиксации. С другой стороны, в логах теперь не будет указано, произошло ли что-то нехорошее, и коммит не был выполнен, или программист просто забыл написать коммит. Это второй случай, который я хочу поймать. - person LiKao; 01.04.2011