Сообщений 23    Оценка 7 [+0/-16]         Оценить  
Система Orphus

Симуляция блока try-finally для С++

Автор: Проскурня М.О.
Опубликовано: 19.03.2004
Исправлено: 10.12.2016
Версия текста: 1.0

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

Рассмотрим пример фрагмента кода, который захватывает ресурсы и обращается к критической функции void critical (char*, int):

      int   numread;
char* buffer = NULL; // инициализация для детектирования возможных измененийint   fd     = -1;   // ... аналогичноtry
{
   buffer = newchar [256];
   fd = open ( "data", "r" );   // открываем файл и читаем по 256 символовwhile ( 0 < ( numread = read ( fd, buffer, 256 ) ) )
   {
       critical ( buffer, numread );  // вызов критической функции
   }
}
catch ( ... )
{
   if ( 0 < fd ) close ( fd );  // освобождение ресурсовif ( buffer ) delete buffer; // ... аналогичноthrow;                       // делегирование исключения внешнему обработчику
}
if ( 0 < fd ) close ( fd );     // освобождение ресурсовif ( buffer ) delete buffer;    // ... аналогично

В этом примере видно, что операции по освобождению ресурсов повторяются два раза: при нормальном ходе выполнения и при возникновении критической ситуации. В некоторые языки программирования для такого случая была введена конструкция вида try-finally, в которой секция finally выполнялась обязательно, как при возникновении исключений, так и при нормальном ходе выполнения программы. Поскольку в языке C++ такой конструкции явно не присутствует, мы можем попытаться описать её самостоятельно, прибегая к помощи макропрепроцессора:

      #define
      finally(Saver) \
catch ( ... ) \
{ \
  Saver \
  throw; \
} \
Saver

Тогда вышеприведенный код можно будет записать следующим образом:

      char* buffer = NULL;
int   fd = -1, numread;
try
{
   buffer = newchar [256];
   fd = open ( "data", "r" );  // открываем файл и читаем по 256 символовwhile ( 0 < ( numread = read ( fd, buffer, 256 ) ) )
   {
       critical ( buffer, numread );  // вызов критической функции
   }
}
finally (   // код освобождения ресурсов помещён в круглые скобки, так какif ( 0 < fd ) close ( fd );  // он является текстовым параметром макросаif ( buffer ) delete buffer; 
)

Как видно из текста макроса, фактическое дублирование кода было переложено на макрогенератор. Следует отметить, что макрос finally «прозрачен» для исключений, поэтому его использование аналогично конструкциям try-finally других языков. Среди достоинств такого подхода можно отметить повышение наглядности кода, уменьшение ошибок вида copy-paste, а к недостаткам следует отнести невозможность пошаговой отладки кода finally и ограничение на использование запятых, которые по синтаксису являются разделителями параметров макроса.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 23    Оценка 7 [+0/-16]         Оценить