Коллеги, хотелось бы поднять тему обработки исключений в С++ приложениях.
При разработке системы отслеживания и обработки ошибок в объектно-ориентированном приложении порою сталкиваешься с дилемой: какой подход и в каком случае применять для решения этих задач. Фактически речь идет о выборе между отлавливанием через try-catch, предпологающим написание классов исключений и обычным анализом возвращаемого статуса выполнения. Кто чем пользуется и в каких случаях пользуется?
Здравствуйте, Slick, Вы писали:
S>Коллеги, хотелось бы поднять тему обработки исключений в С++ приложениях. S>При разработке системы отслеживания и обработки ошибок в объектно-ориентированном приложении порою сталкиваешься с дилемой: какой подход и в каком случае применять для решения этих задач. Фактически речь идет о выборе между отлавливанием через try-catch, предпологающим написание классов исключений и обычным анализом возвращаемого статуса выполнения. Кто чем пользуется и в каких случаях пользуется?
Хотя ву некоторых случаях лучше применять исключения (например в случае очень редкой ошибки или в конструкторе), хочу отметить, что исключение, это разновидность GoTo, это очень шрусный оператор
If the milk turns out to be sour,
I ain't the kind of pussy to drink it
Здравствуйте, Slick, Вы писали:
S>Коллеги, хотелось бы поднять тему обработки исключений в С++ приложениях. S>При разработке системы отслеживания и обработки ошибок в объектно-ориентированном приложении порою сталкиваешься с дилемой: какой подход и в каком случае применять для решения этих задач. Фактически речь идет о выборе между отлавливанием через try-catch, предпологающим написание классов исключений и обычным анализом возвращаемого статуса выполнения. Кто чем пользуется и в каких случаях пользуется?
Сделал класс HResultTest, поведение которого управляется тучей флагов.
Перекрыл ему оператор присваивания, так, что HResultTest::operator=(HRESULT) проверяет входное значение и может вывалить Assertion Fault и/или метнуть исключение.
Дальше с помощью макросов и бениной матери можно делать чудеса.
Здравствуйте, Кодт, Вы писали:
К>Сделал класс HResultTest, поведение которого управляется тучей флагов. К>Перекрыл ему оператор присваивания, так, что HResultTest::operator=(HRESULT) проверяет входное значение и может вывалить Assertion Fault и/или метнуть исключение.
К>Дальше с помощью макросов и бениной матери можно делать чудеса.
Александреску в своей последней статье Enforcements все это сделал очень красиво.
Здравствуйте, MaximE, Вы писали:
ME>Александреску в своей последней статье Enforcements все это сделал очень красиво.
Действительно
Единственный нюанс, который хочется отметить. При работе с COM нужно запоминать и проверять HRESULT,
так что enforcement "по-тупому" выглядит примерно так
— и по части допустимых значений (например, разборщик строки имеет право в реальной жизни вернуть S_OK или E_FAIL, а все остальное будет считаться неприятным сюрпризом),
— и по способу обработки: в одном и том же блоке
— — некоторые ошибки подлежат отладке (к примеру, мы подаем на вход парсера строковый литерал, и ожидаем исключительно S_OK)
— — другие мечут исключения (выделение памяти или создание объекта в родном COM-сервере)
— — третьи обрабатываются или игнорируются (на входе парсера — неизвестная строка)
то приходится либо завести тучу разных SmartHRESULT с разными политиками, либо настраивать политики на месте:
Здравствуйте, Slick, Вы писали:
S>Коллеги, хотелось бы поднять тему обработки исключений в С++ приложениях. S>При разработке системы отслеживания и обработки ошибок в объектно-ориентированном приложении порою сталкиваешься с дилемой: какой подход и в каком случае применять для решения этих задач. Фактически речь идет о выборе между отлавливанием через try-catch, предпологающим написание классов исключений и обычным анализом возвращаемого статуса выполнения. Кто чем пользуется и в каких случаях пользуется?
Обычно получаетчя примерно так:
if (OK==f(x))
if (OK==f2(x))
if (OK==f3(x))
else
else
else
или для красоты:
bool success;
и то же самое без вложенности
когда это вырастает больше, чем на 1/2 страницы, оказывается очень полезным еще вчера подумать про исключения:
try
f(x)
f2(x)
f3(x)
catch
вот такую вот вывел эвристику
мгновенность операции компенсируется бесконечностью цикла
Здравствуйте, Slick, Вы писали:
S>Коллеги, хотелось бы поднять тему обработки исключений в С++ приложениях. S>При разработке системы отслеживания и обработки ошибок в объектно-ориентированном приложении порою сталкиваешься с дилемой: какой подход и в каком случае применять для решения этих задач. Фактически речь идет о выборе между отлавливанием через try-catch, предпологающим написание классов исключений и обычным анализом возвращаемого статуса выполнения. Кто чем пользуется и в каких случаях пользуется?
Я делю ошибки на два вида:
1) те которые я ожидаю
2) и те которые маловероятны
Первые — лучше обрабатывать обычным способом (по возвращаемому результату)
Вторые — эксепшенами.
Например:
Работа с файлами:
Вполне вероятно, что может произойти ошибка при открытии файла и эксепшен лучше не использовать.
Если уж мы его благополучно открыли, то маловероятно что произойдет ошибка при чтении/записи лучше использовать эксепшены.
Для ошибок, которые обнаруживаются отладочным кодом — лучше тоже использовать эксепшены (типа: InternalError).
Здравствуйте, Slick, Вы писали:
S>Коллеги, хотелось бы поднять тему обработки исключений в С++ приложениях. S>При разработке системы отслеживания и обработки ошибок в объектно-ориентированном приложении порою сталкиваешься с дилемой: какой подход и в каком случае применять для решения этих задач. Фактически речь идет о выборе между отлавливанием через try-catch, предпологающим написание классов исключений и обычным анализом возвращаемого статуса выполнения. Кто чем пользуется и в каких случаях пользуется?
Imho исключения весчь исключительно полезная Можно в одном месте централизировать все обработку ошибок, не надо заводить всякие разные коды ошибок и плюс ещё куча всяких рулезов Только нужно юзать Resource Acquisition Is Initialisation (RAII), а то проблемы гарантированы.
Здравствуйте, Slick, Вы писали:
S>Коллеги, хотелось бы поднять тему обработки исключений в С++ приложениях. S>При разработке системы отслеживания и обработки ошибок в объектно-ориентированном приложении порою сталкиваешься с дилемой: какой подход и в каком случае применять для решения этих задач. Фактически речь идет о выборе между отлавливанием через try-catch, предпологающим написание классов исключений и обычным анализом возвращаемого статуса выполнения. Кто чем пользуется и в каких случаях пользуется?
В принципе, исключения и их использование обсуждаются в книге Рихтера (Applied MS .NET Framework Programming), правда для .NET-а , но я думаю подход в C++ и .NET к обработке ошибок похож (хотя в .NEТ исключения быстрее, следовательно их можно более аггресивно использовать.
Здравствуйте, Kuzma K., Вы писали:
KK>когда это вырастает больше, чем на 1/2 страницы, оказывается очень полезным еще вчера подумать про исключения: KK>
KK>try
KK> f(x)
KK> f2(x)
KK> f3(x)
KK>catch
KK>
KK>вот такую вот вывел эвристику
Во первых, вы используете разновидность GoTo, а это очень плохой оператор, а во вторых, вы увеличиваете вероятность падения программы — как известно, два не пойманных исключения = аварийному завершению программы.
Если я забуду обработать одну ошибку в функции, то у меня нарушится логика работы, и вероятно, не будет ни чего плохого, но если я забуду обработать два исключения ...
Так что уж лучше без них.
Правдо те ошибка, что мало вероятны, можно и обработать исключениями
Удалено избыточное цитирование. -- ПК.
If the milk turns out to be sour,
I ain't the kind of pussy to drink it
Здравствуйте, skyline, Вы писали:
S>Здравствуйте, Slick, Вы писали:
S>Хотя ву некоторых случаях лучше применять исключения (например в случае очень редкой ошибки или в конструкторе)
Я считаю, что исключения в конструкторах применять нельзя, нет гарантии, что будет вызван деструктор (он и не должен вызываться, объект с точки зрения логики еще не созался полностью). Если в конструкторе Вы выделили память, а дальте эксепшен, то эта память так и повиснет в воздухе. Даже дядяка СтраусМертв пишет "всю сложную инициализацию в отдельный метод а конструктор и деструктор без исключений нужно делать". Он умный, он знает "когда страус умер"
> Я считаю, что исключения в конструкторах применять нельзя, нет гарантии, что будет вызван деструктор (он и не должен вызываться, объект с точки зрения логики еще не созался полностью). Если в конструкторе Вы выделили память, а дальте эксепшен, то эта память так и повиснет в воздухе.
dtor действительно не вызывается для неполностью сконструированного объекта. А для того, чтобы не вешать память, нужно пользоваться std::auto_ptr.
Здравствуйте, template, Вы писали:
T>Я считаю, что исключения в конструкторах применять нельзя, нет гарантии, что будет вызван деструктор (он и не должен вызываться, объект с точки зрения логики еще не созался полностью). Если в конструкторе Вы выделили память, а дальте эксепшен, то эта память так и повиснет в воздухе. Даже дядяка СтраусМертв пишет "всю сложную инициализацию в отдельный метод а конструктор и деструктор без исключений нужно делать". Он умный, он знает "когда страус умер"
Исключения это вообще единственный надежный способ вернуть ошибку произошедшую в конструкторе (согласно Страуструпу же).
Трудно написать более менее серьезный проект, в котором конструкторы всех обьектов завершаются с успехом в любом случае.
Как обезопасить себя от ликов, Виталий Заболотный уже сказал (std::auto_ptr).
Здравствуйте, skyline, Вы писали:
S>Во первых, вы используете разновидность GoTo, а это очень плохой оператор,
оператор goto считается плохим, когда он применяется в программе на языке высогого уровня, т.к. нарушает программную логику
это совершенно очевидный и давно доказанный факт, я думаю ты с этим спорить не будешь.
снижение читаемости программы, путанница, вероятность ошибки и т.п.
но такой эффект goto имеет именно в языках уровня
в ассемблере, через который эти операторы реализованы это абсолютно нормальный и неизбежный оператор, и его применеие совершенно оправдано
целый ряд операторов языка высого уровня, на уровне ассемблера реализованы через goto — это и for/while и if и try-catch и еще много
но вот они то как раз опасными, и разрушающими логику не являются, т.к. здесь логика выполнения базируется на других критериях нежели в ассемблере
поэтому справедливо упрекать за применения goto а чистом виде а не за применеие операторов, реализованных через goto
следовательно утверждение "поскольку try-catch реализован через goto то try-catch это дерьмо" не выдерживает никакой критики
Здравствуйте, skyline, Вы писали:
S>Во первых, вы используете разновидность GoTo, а это очень плохой оператор,
Причем тут goto? Исключение разматывает стек. S>а во вторых, вы увеличиваете вероятность падения программы — как известно, два не пойманных исключения = аварийному завершению программы.
Поправка одно. Но как я понял ты имееш в виду исли при раскрутки стека исключением деструктор кинул свое то тогда действительно крах. По этому в деструкторах НЕЛЬЗЯ кидать исключения. S>Если я забуду обработать одну ошибку в функции, то у меня нарушится логика работы, и вероятно, не будет ни чего плохого, но если я забуду обработать два исключения ...
Да и проявлятся она будет после дождичка в четверг. Именно такие ошибки труднее всего поймать, а если у тебя рухнула программа то ты покрайней мере будешь знать что она есть. А если это произошло под отладчиком то еще будешь знать где именно. S>Так что уж лучше без них.
Нетуж лучше с ними. S>Правдо те ошибка, что мало вероятны, можно и обработать исключениями
Хотя и злоупотреблять тоже не стоит.
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн