Аннотация:
В некоторых языках программирования существует конструкция вида try-finally, в которой секция finally выполнялась обязательно, как при возникновении исключений, так и при нормальном ходе выполнения программы. Поскольку в языке C++ такой конструкции явно не присутствует, мы можем попытаться описать её самостоятельно, прибегая к помощи макропрепроцессора.
Здравствуйте, Проскурня М.О., Вы писали:
ПМО>Статья: ПМО>Симуляция блока try-finally для С++
ПМО>Авторы: ПМО> Проскурня М.О.
ПМО>Аннотация: ПМО>В некоторых языках программирования существует конструкция вида try-finally, в которой секция finally выполнялась обязательно, как при возникновении исключений, так и при нормальном ходе выполнения программы. Поскольку в языке C++ такой конструкции явно не присутствует, мы можем попытаться описать её самостоятельно, прибегая к помощи макропрепроцессора.
И она там нафиг не нужна, поскольку есть деструкторы. Мне вот интересно, почему писать в каждом месте где нужно, скажем, закрыть файл, код для закрытия файла это не напряжно, а написать один раз деструктор -- это страшно тяжело.
Здравствуйте, Проскурня М.О., Вы писали:
ПМО>В некоторых языках программирования существует конструкция вида try-finally, в которой секция finally выполнялась обязательно, как при возникновении исключений, так и при нормальном ходе выполнения программы. Поскольку в языке C++ такой конструкции явно не присутствует, мы можем попытаться описать её самостоятельно, прибегая к помощи макропрепроцессора.
О Великий и Могучий яз..., то есть препроцессор
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Проскурня М.О., Вы писали:
ПМО>Статья:
ПМО>Авторы: ПМО> Проскурня М.О.
ПМО>Аннотация: ПМО>В некоторых языках программирования существует конструкция вида try-finally, в которой секция finally выполнялась обязательно, как при возникновении исключений, так и при нормальном ходе выполнения программы. Поскольку в языке C++ такой конструкции явно не присутствует, мы можем попытаться описать её самостоятельно, прибегая к помощи макропрепроцессора.
Конструкции try-finally ИМХО встречаются гораздо реже нежели try-catch-finaly. И такие экзотические catch, что делегируют исключение внешнему обработчику достаточно редкие явления, я бы заюзал деструктор, если мне надо освободить ресурсы, а исключения бы просто не ловил.
/**
* у человека столько проблем, сколько он их себе создает
*/
Здравствуйте, Проскурня М.О., Вы писали:
ПМО>Статья:
ПМО>Авторы: ПМО> Проскурня М.О.
ПМО>Аннотация: ПМО>В некоторых языках программирования существует конструкция вида try-finally, в которой секция finally выполнялась обязательно, как при возникновении исключений, так и при нормальном ходе выполнения программы. Поскольку в языке C++ такой конструкции явно не присутствует, мы можем попытаться описать её самостоятельно, прибегая к помощи макропрепроцессора.
Далеко не все препроцессоры прожуют такую конструкцию.
Про деструкторы и т.д. уже сказали
Здравствуйте, Проскурня М.О., Вы писали:
ПМО>Аннотация: ПМО>В некоторых языках программирования существует конструкция вида try-finally.. Поскольку в языке C++ такой конструкции явно не присутствует, мы можем попытаться описать её самостоятельно, прибегая к помощи макропрепроцессора
У Andrei Alexandrescu есть статья "Change the Way You Write Exception-Safe Code — Forever", в которой приведено элегантное и удобное на практике решение, позволяющее эмулировать finally с помощью шаблонного класса ScopeGuard.
Киса, скажите как художник — художнику: вы рисовать умеете?
Ну и мои 3 коп.
Во-1х запятая в таком finally-блоке недопустипа, поскольку будет считаться разделителем аргументов макроса.
Во-2х finally предполагает что переменные определяются отдельно до их использования — перед началом try-блока, что является плохим стилем в C++.
Если немного присмотреться, то видно, что в статье пример не совсем в тему. Используется ручное управление памятью.
В данном примере достаточно механизмов SEH(для вындоуза). Вот как это будет выглядеть.
char* buffer = NULL;
int fd = -1, numread;
__try
{
buffer = new char [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;
}
С макросом из статьи много проблем.
1. Не факт что все выражения подымутся пропроцессором.
2. Количество кода обработки удваивается. Не много, но часто это неприятно.
3. В макросе есть throw. Этого не следует делать. Программист должен сам расставлять throw.
4. В ряде случаев catch(...) никуда не должен передавать исключение.
На С++ проще использовать коллекции на стеке, нежели динамически управлять памятью. Эффект тот же.
Роль finally сыграет деструктор, который вызовется в любом случае при локальной или глобальной раскрутке.
Посему макрос в статье нужен лишь для того, что бы привлечь читателя к проблеме использования механизма исключений.
Re: Симуляция блока try-finally для С++
От:
Аноним
Дата:
23.03.04 09:12
Оценка:
Здравствуйте, Проскурня М.О., Вы писали:
ПМО>Статья:
1) Отличный от привычного синтаксис.
2) Когда я использую "нормальный" finally — языковая среда гарантирует, что код, заключенный в него будет вызван (почти) всегда.
try {
char* sz = (0, new char[128]);
// use it here
}
catch(const bad_alloc&) {
// memory exhausted, process it
throw;
}
finally (
// может ли случиться так, что не попадем сюда? да.
)
Здравствуйте, Plutonia Experiment, Вы писали:
PE>С макросом из статьи много проблем. PE>1. Не факт что все выражения подымутся пропроцессором. PE>2. Количество кода обработки удваивается. Не много, но часто это неприятно. PE>3. В макросе есть throw. Этого не следует делать. Программист должен сам расставлять throw. PE>4. В ряде случаев catch(...) никуда не должен передавать исключение.
Еще момент, самый основной, который я упустил.
finally должен срабавтывать и в случае return в блоке try. В случае с макросом это не так.
А> 2) Когда я использую "нормальный" finally — языковая среда гарантирует, что код, заключенный в него будет вызван (почти) всегда.
А>try { А> char* sz = (0, new char[128]); А> // use it here А>} А>catch(const bad_alloc&) { А> // memory exhausted, process it А> throw; А>} А>finally ( А> // может ли случиться так, что не попадем сюда? да. А>)
Я привёл пример симуляции конструкции try-finally, а не try-catch-finally. Ваш код должен содержать два вложенных блока try { try {} catch {} } finally (), иначе ерунда получается.
2 Шахтер:
Ш>И она там нафиг не нужна (симуляция try-finally), поскольку есть деструкторы. Мне вот интересно, почему писать в каждом месте где нужно, скажем, закрыть файл, код для закрытия файла это не напряжно, а написать один раз деструктор -- это страшно тяжело.
Отвечаю:
Иногда методы используют не обёрнутые в классы ресурсы. Если есть возможность обернуть, то я с вами согласен.
Здравствуйте, Atomic Max, Вы писали:
AM>Я привёл пример симуляции конструкции try-finally, а не try-catch-finally. Ваш код должен содержать два вложенных блока try { try {} catch {} } finally (), иначе ерунда получается.
Ты перечитай его сообщение. Макрос дает всего лишь catch(...).
А try-finally даже наполовину не получилась. Не все отслеживается. Толку от такого finally ?
Здравствуйте, Atomic Max, Вы писали:
AM>2 Шахтер:
Ш>>И она там нафиг не нужна (симуляция try-finally), поскольку есть деструкторы. Мне вот интересно, почему писать в каждом месте где нужно, скажем, закрыть файл, код для закрытия файла это не напряжно, а написать один раз деструктор -- это страшно тяжело.
AM>Отвечаю: AM>Иногда методы используют не обёрнутые в классы ресурсы. Если есть возможность обернуть, то я с вами согласен.
Здравствуйте, Atomic Max, Вы писали:
AM>Иногда методы используют не обёрнутые в классы ресурсы. Если есть возможность обернуть, то я с вами согласен.
1)Вы не могли бы привести пример ресурсов которые _нельзя_ безопасно обернуть используя идиому деструктора?
2)Фокус с finally на основе catch(...) будет работать не во всех компиляторах. В частости попробуйте в gcc такой код
try
{
int* pInt = 0;
*pInt = 1;
}
catch(...)
{
std::cout<<"Yes! I have caught it!\n";
}
блок внутри catch(...) гарантированно не выполниться. В общем случае catch(...) перехватывает все необработанные раннее иключения. Т.е. те которые генерируются throw. То что структурные исключения в VC++ перехватываются в catch(...) — просто бонус microsoft, а не свойство языка. Конструкция try-finally есть в Java, но там нет деструторов (таких, к как в с++). А в с++ эта конструкция просто не нужна. К тому же она строиться целиком на макросах
Здравствуйте, Atomic Max, Вы писали:
AM>2 Шахтер:
Ш>>И она там нафиг не нужна (симуляция try-finally), поскольку есть деструкторы. Мне вот интересно, почему писать в каждом месте где нужно, скажем, закрыть файл, код для закрытия файла это не напряжно, а написать один раз деструктор -- это страшно тяжело.
AM>Отвечаю: AM>Иногда методы используют не обёрнутые в классы ресурсы. Если есть возможность обернуть, то я с вами согласен.
Если в этом коде ловить исключения, то будет полный аналог try...catch...finally.
//______________________________________________________________________________Function
//
// int CSoundConverter::ConvertMediaFileToMp3(HMMIO hInputFile, HMMIO hOutputFile,
// WAVEFORMATEX* pSrcFormat, WAVEFORMATEX* pDstFormat)
//
// This function converts WAV file to MP3 using
// acmXXX functions. It gets source format from the input file.
//int CSoundConverter::ConvertMediaFileToMp3(HMMIO hInputFile, HMMIO hOutputFile,
WAVEFORMATEX* pSrcFormat, WAVEFORMATEX* pDstFormat)
{
//__________________________
//
// class vars
//
// Local class to simplify cleanup on function exit
//class vars
{
public:
HACMSTREAM stream;
BYTE pInputBuf [CONVERSION_BUFFER_MAX_LEN]; // conversion buffer
BYTE* pOutputBuf;
DWORD dwInputBufferLen; // input buffer lenght that will
// be used in conversion
DWORD dwOutputBufferLen; // output buffer lenght that will
// be used in conversion
vars()
{
CSoundFormatDetails::hacmDriver = NULL;
stream = NULL;
pOutputBuf = NULL;
}
~vars()
{
if (pOutputBuf)
delete [] pOutputBuf;
if (stream)
{
//
// Close conversion stream
//
acmStreamClose(stream, 0);
}
if (CSoundFormatDetails::hacmDriver)
{
//
// Close driver handle
//
acmDriverClose(CSoundFormatDetails::hacmDriver, 0);
}
}
} local;
//
// Open ACM driver for conversion
//
MMRESULT res;
res = acmDriverOpen ( &CSoundFormatDetails::hacmDriver,
CSoundFormatDetails::hacmDriverID,
0
);
if (res)
return CONV_CODEC_UNAVAILABLE;
//
// Open conversion stream
//
res = acmStreamOpen(&local.stream, CSoundFormatDetails::hacmDriver,
pSrcFormat, pDstFormat, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME);
if (res)
{
OutputDebugString("acmStreamOpen() has failed!\n");
return CONV_UNABLE_PRODUCE_DST_FORMAT;
}
//
// Calculate input buffer size (counting input blocks granuality)
//
local.dwInputBufferLen =
((unsigned long)(CONVERSION_BUFFER_MAX_LEN / pSrcFormat->nBlockAlign)) * (pSrcFormat->nBlockAlign);
//
// See what buffers sizes are required for conversion
//
res = acmStreamSize(local.stream, local.dwInputBufferLen, &local.dwOutputBufferLen, ACM_STREAMSIZEF_SOURCE);
if (res)
{
OutputDebugString("acmStreamSize() has failed!\n");
return CONV_UNABLE_PRODUCE_DST_FORMAT;
}
else
{
//
// Allocate memory for output buffer
//
local.pOutputBuf = new BYTE [local.dwOutputBufferLen];
if (!local.pOutputBuf)
return CONV_NO_MEMORY;
}
.....
ПМО>В некоторых языках программирования существует конструкция вида try-finally...
А в С++ НЕ существует! И не потому, что авторы не доглядели — Страуструп пишет, что это было полностью осознанное решение. Нужда в finally пропадает абсолютно при использовании техники RAII (выделение ресурса есть инициализация). finally в Java по сути только усложняет внешний вид кода. При RAII нам вообще локально нет нужды писать никакие try и поэтому код
Здравствуйте, Шахтер, Вы писали:
Ш>И она там нафиг не нужна, поскольку есть деструкторы. Мне вот интересно, почему писать в каждом месте где нужно, скажем, закрыть файл, код для закрытия файла это не напряжно, а написать один раз деструктор -- это страшно тяжело.
Полностью поддерживаю. Да и вообще — лепят горбатого к стенке — то проперти, то try/finally,...
Здравствуйте, Plutonia Experiment, Вы писали:
PE>Здравствуйте, Atomic Max, Вы писали:
AM>>Отвечаю: AM>>Иногда методы используют не обёрнутые в классы ресурсы.
PE>В этом случае рулит SEH
SEH рулит в коде на C но не C++ т.к. блок обработки SEH ничего не знает про деструкторы C++