Re: Зачем надо throw new CMyException
От: GvozdodeR  
Дата: 11.03.03 21:59
Оценка:
Здравствуйте, Borisman, Вы писали:

B>На вопрос, как нужно выбрасывать исключения, есть два мнения:

B>1) throw CMyException(...)
B>2) throw new CMyException(...)

B>Бойцы, у кого какое мнение?


В пользу второго способа, imho, есть только один разумный довод (производительность я не считаю, ибо ей с исключениями не по пути), с которым я буквально сегодня столкнулся. Если исключение выбрасывается с помощью способа №1 из .dll, а ловится, к примеру, в .exe, причем при раскрутке стека та самая .dll выгружается, то произойдет Access Violation в catch-блоке. В таком случае, лучший вариант -- выбросить указатель на исключение, а не экземпляр. Я обошел эту ситуацию, обернув код выгрузки .dll условием if(!std::uncaught_exception())

Огромный плюс в первом способе выброса исключения в том, что так ведет себя стандартная библиотека. Поэтому, если придется использовать вариант №2, то возникнут проблемы с отловом ее исключений, а также многих других библиотек (boost, например)
Re[5]: Зачем надо throw new CMyException
От: Borisman  
Дата: 12.03.03 05:43
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Хотелось бы отметить, что у передачи объекта-исключения указателем есть и определенные преимущества (не умаляя недостатков ).


А>Например, при передаче значением выполнить что-то вроде InnerException в .Net (т. е. обернуть одно исключение другим, так что исходное исключение становится причиной исключения на более высоком уровне абстракции) можно, но становится некрасиво.


Когда я впервый прочитал и понял, зачем в .NET нужен InnerException, я задумался, как же я всю жизнь обходился ьез InnerEcxeption. Вообще всю эту микробучу на форуме я затеял потому, что сейчас пытаюсь придумать свою библиотеку исключений под С++ где была бы поддержка InnerException. Есть по сути два варианта:
1) Либо бросать указатели на динамически создаваемые исключения и иметь затем проблемы с их удалением (зато быстро работает).
2) Либо бросать копии исключений и обертывать их другими исключениями и иметь проблемы сос скоростью работы (зато никаких проблем с удалением исключений).

Лично мне второй вариант кажется предпочтительнее. Исключения на то и исключения, чтобы происходить сравнительно редко. Вообще обработка любого исключения — дело накладное и не особо тут выиграешь на кописровании классов.

Под влиянием общественности пришел в голову третий вариант — можно кидать объекты, ловить ссылки и вот эти ссылки(а не указатели) и оборачивать другими исключениями. Вот так надо и сделать, по-моему.
Re[2]: Зачем надо throw new CMyException
От: Borisman  
Дата: 12.03.03 05:46
Оценка:
К>Если ты будешь передавать исключение дальше, да еще, завернув его в другое, то, возможно, второй подход будет лучше. Но я так никогда не делаю. И вообще не понимаю, суть обсуждения производительности блоков try-catch. Кому

Согласен, см здесь http://www.rsdn.ru/forum/Message.aspx?mid=211994&only=1
Автор: Borisman
Дата: 12.03.03
Re[10]: Re[2]: Зачем надо throw new CMyException
От: Borisman  
Дата: 12.03.03 06:00
Оценка:
Здравствуйте, Ведмедь, Вы писали:

В>а если так


В>
В>void InnerCheck2( a )
В>{
В>  if( a == 1 )
В>     throw "oooops";
В>  throw L"OOOOPS";
В>}

В>void InnerCheck1( a )
В>{
В>   InnerCheck2(a);
В>   throw (int)a;
В>}

В>void SomeCheck( a )
В>{
В>     InnerCheck1(a);
В>}

В>
В>void SomeFunction
В>{
В>  try
В>  {
В>    for()
В>      ...
В>     for()
В>    {
В>            SomeCheck( a );
В>    }
В>  }catch( int e )
В>  {
В>    printf( "some var1 = %d", i );
В>  }catch( char* e )
В>  {
В>    printf( " some var2 = %s",e );
В>  }
В>  catch( wchar_t* e )
В>  {
В>     printf( " some var2 = %s",e );
В>     throw e;// перекинули на обработку дальше
В>  }
В>  printf( "final" );
В>}
В>


Забавно. Но по-моему кидать что-то типа
 throw "Приветик из Австралии!"
весьма надуманно. Ну кому может понадобиться такой приветик? Кидают как правило что-нить вроде CArgumentException("IRQ_LEVEL", 0, 1) и в самых крайних случаях.

Я все это говорю к тому, что спор о том, что лучше — throw или goto достаточно бессмысленный. Исключения должны кидаться в ИСКЛЮЧИТЕЛЬНЫХ случаях, а не для выхода из каких-то там вложенных циклов. Если ситуация, когда нужно выйти из кучи вложеннных циклов (или там, функций) — достаточно типичная, пусть даже ошибочная, и часто происходящая, то не следует по-моему пользоваться исключениями. Выйдите как все нормальные люди выходят — через дверь (я имею в виду старый добрые операторы break, return и т.д. Насчет goto — я лично против. Вот уже 5 лет не написал ни одного оператора goto и прекрасно себя чуствую).

Типичный пример, который вычитан мной из МСДН.
Пользователь должен авторизироваться при коннекте к серверу. Можно, конечно, сделать так:

void Logon(const CString & name, const CString &passwd)
{
  ...
  if (!DB_HAS_ACCESS(name, passwd, currenttime, ...)) throw BadLogon();
}


Но концептуально это глупо. Ведь неверное набирание пароля — дело лостаточно обычное, чего тут за голову хвататься и исключениями кидаться.
Лучше все-таки по старинке:

void Logon(const CString & name, const CString &passwd)
{
  ...
  return DB_HAS_ACCESS(name, passwd, currenttime, ...);
}


Каждый инструмент должен использоваться по назначению.

Удалено избыточное цитирование. -- ПК.
Re[2]: Re: Зачем надо throw new CMyException
От: Borisman  
Дата: 12.03.03 06:12
Оценка:
sev>После некоторых раздумий я реализовал решение, коим весьма теперь доволен.
sev>Все проблемы с автоматическим удалением исключения в нём решены, а также
sev>добавлена возможность "вложенности" исключений в стиле .NET.

sev>Решение можно посмотреть в исходном тексте вот здесь:

sev>http://www.demoforge.com/misc/SmartExceptions.h

Да, действительно неплохо! Недостатка на самом деле два — второй — использование СException, a, значит, привязка к MFC. Но иначе все дела со снимком стека пришлось бы самому кодить. Очень, очень неплохо, мне понравилось. Именно что-то подобное я и хотел сам сделать, потому и вопрос такой задал.

Вообще, sev, вот Вам еще пища для размышлений:
http://oop.rosweb.ru/users/v/vbelkin/yaace.htm
Почитайте, думаю Вам будет интересно.
Re[3]: Re: Зачем надо throw new CMyException
От: SchweinDeBurg Россия https://zarezky.spb.ru/
Дата: 12.03.03 06:27
Оценка:
Здравствуйте, Borisman, Вы писали:

B>...использование СException, a, значит, привязка к MFC...


Там же свой CException определяется, причем здесь MFC?
- Искренне ваш, Поросенок Пафнутий
Re[4]: Re: Зачем надо throw new CMyException
От: Borisman  
Дата: 12.03.03 06:52
Оценка:
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Здравствуйте, Borisman, Вы писали:


B>>...использование СException, a, значит, привязка к MFC...


SDB>Там же свой CException определяется, причем здесь MFC?

Да, беру свои слова обратно. Есть там CException. Но там и CString есть, хотя это уже не принципиально — можно в конце концов подправить чуток и обойтись без MFC. Я так и сделаю, если автор будет не против.
Re[5]: Re: Зачем надо throw new CMyException
От: SchweinDeBurg Россия https://zarezky.spb.ru/
Дата: 12.03.03 06:57
Оценка:
Здравствуйте, Borisman, Вы писали:

B>...Но там и CString есть...

Есть у меня сильное подозрение, что CString там тоже авторский. Хотелось бы, конечно, услышать комментарии самого автора.
- Искренне ваш, Поросенок Пафнутий
Re[11]: Re[2]: Зачем надо throw new CMyException
От: Areex  
Дата: 12.03.03 07:31
Оценка:
Здравствуйте, Borisman, Вы писали:

B>Типичный пример, который вычитан мной из МСДН.

B>Пользователь должен авторизироваться при коннекте к серверу. Можно, конечно, сделать так:

B>
B>void Logon(const CString & name, const CString &passwd)
B>{
B>  ...
B>  if (!DB_HAS_ACCESS(name, passwd, currenttime, ...)) throw BadLogon();
B>}
B>


B>Но концептуально это глупо. Ведь неверное набирание пароля — дело лостаточно обычное, чего тут за голову хвататься и исключениями кидаться.

B>Лучше все-таки по старинке:

Чем глупо?
Именно так и надо делать. Концептуально функция должна возвращать результат, а не сообщение об ощибке.
"We charge!"
Re[6]: Зачем надо throw new CMyException
От: Аноним  
Дата: 12.03.03 07:36
Оценка:
Здравствуйте, Borisman, Вы писали:

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


Не выйдет. В catch-блоке ты получаешь ссылку на временный, в сущности, объект. Сохранение ссылки на него — все одно, что сохранение ссылки на автоматический объект после выхода его из области видимости.
Re[11]: Re[2]: Зачем надо throw new CMyException
От: Ведмедь Россия  
Дата: 12.03.03 08:20
Оценка:
Здравствуйте, Borisman, Вы писали:


B>
B>void Logon(const CString & name, const CString &passwd)
B>{
B>  ...
B>  if (!DB_HAS_ACCESS(name, passwd, currenttime, ...)) throw BadLogon();
B>}
B>


B>Но концептуально это глупо. Ведь неверное набирание пароля — дело лостаточно обычное, чего тут за голову хвататься и исключениями кидаться.

B>Лучше все-таки по старинке:

B>
B>void Logon(const CString & name, const CString &passwd)
B>{
B>  ...
B>  return DB_HAS_ACCESS(name, passwd, currenttime, ...);
B>}
B>


B>Каждый инструмент должен использоваться по назначению.


НА мой взгляд в некоторых случаях наоборот. К примеру, если пароль проверяется в более глубоких функциях, то проверять каждый раз возвращяемое значение не особо читабельно, можно забыть его проверить, помнить, что кадый нумер ошибки значит и отдельно его обрабтывать. А исключение кинул "неверное имя или пароль" и тебя больше не волнует, а вдруг кто то заюыл проверить возвращяемый результат. Оно обработалось обработчиком нужного типа и т.п. и т.д.
Да пребудет с тобой Великий Джа
Re[12]: Re[2]: Зачем надо throw new CMyException
От: Borisman  
Дата: 12.03.03 08:39
Оценка:
Здравствуйте, Areex, Вы писали:

A>Чем глупо?

A>Именно так и надо делать. Концептуально функция должна возвращать результат, а не сообщение об ощибке.

Ошибка — тоже результат.

Тут нет, конечно, единого правила. Но все-таки не следует часто случающиеся ошибки кодировать исключениями. Все надо делать в соответствии со зравым смыслом, вот только здравый смысл у каждого свой
Re[13]: Re[2]: Зачем надо throw new CMyException
От: Areex  
Дата: 12.03.03 09:31
Оценка:
Здравствуйте, Borisman, Вы писали:

B>Здравствуйте, Areex, Вы писали:


A>>Чем глупо?

A>>Именно так и надо делать. Концептуально функция должна возвращать результат, а не сообщение об ощибке.

B>Ошибка — тоже результат.


Нет, ошибка это ошибка. Функция же может только вернуть результат, который можно трактовать как ошибку.
И возникает головная боль со всякими кодами ошибок и прочими GetLastError.

B>Тут нет, конечно, единого правила. Но все-таки не следует часто случающиеся ошибки кодировать исключениями. Все надо делать в соответствии со зравым смыслом, вот только здравый смысл у каждого свой


Ты не аргументировал, почему не следует. Обработка ошибок прямое назначение исключений. Этак половину c++ можно не использовать, мало ли у кого какой здравый смысл.
"It's only a naga."
Re[3]: Зачем надо throw new CMyException
От: Колян  
Дата: 12.03.03 09:49
Оценка:
Здравствуйте, Ведмедь, Вы писали:

К>>Если ты будешь передавать исключение дальше, да еще, завернув его в другое, то, возможно, второй подход будет лучше. Но я так никогда не делаю. И вообще не понимаю, суть обсуждения производительности блоков try-catch. Кому интересно, насколько быстро моя программа может сбоить???


В>Кстати не совсем верно. В данном вопросе да, производительность значения не имеет. Я тоже считаю, что если произошло исключение, то обработать его в 90 процентов случае время есть и можно уже не торопиться Но факт в том что try/catch не в лучшей сторону влияет на производительность, не зависимо от того, происходит или нет исключение. А вот это не приятно.


Я так и не понял, что не совсем верно?
Кстати, скорость алгоритма при использовании try-catch падает не немножко, а существенно. Я не так давно пробовал раскрутить стек с помощью исключений в одном большом проекте. Было место, где этот подход выглядел сипатичнее и если разруливать ситуацию стандартно, то надо было писать много различных проверок (во многих местах this мог стать вдруг инвалидным). Попробовал.

Этот блок стал занимать 12% от времени работы всего алгоритма! (измерял TrueTime-ом)

Конечно, я его убрал. И зарекся от использования исключений без надобности.
Так что, не наступайте на грабли
Re[3]: Re: Зачем надо throw new CMyException
От: sev http://www.demoforge.com
Дата: 12.03.03 10:19
Оценка:
sev>>Решение можно посмотреть в исходном тексте вот здесь:
sev>>http://www.demoforge.com/misc/SmartExceptions.h
> Да, действительно неплохо! Недостатка на самом деле два — второй -
> использование СException, a, значит, привязка к MFC. Но иначе все дела со
> снимком стека пришлось бы самому кодить. Очень, очень неплохо, мне
> понравилось. Именно что-то подобное я и хотел сам сделать, потому и вопрос
> такой задал.

СException там свой, если кого волнуют конфликты с MFC (я его не использую),
то можно погрузить это всё в namespace. CString взят из ATL/MFC 7.

Вообще говоря, структура самого СException здесь непринципиальна,
главное, что есть иерархия этих СException (соответственно, необходим
виртуальный деструктор) и дублирующая иерархия СExceptionPtr, чтобы
обеспечить полиморфизм в catch(). На самом деле СException это даже
не exception в том смысле, что его объекты никогда не бросаются

Касаемо new: подразумевается, что используется run-time с бросающим
new. Т.е. std::bad_alloc нужно ловить в отдельном catch, либо можно
унаследовать CExceptionPtr от std::exception, тогда ловить их все в одной
куче, но при этом не будет доступа к CException. В общем, не самая удачная
идея. Я предпочитаю реагировать на std::bad_alloc выбросом сообщения
"Шеф, усё пропало!" и не утруждать себя дальнейшей диагностикой.
Главное, что если мы собирались бросить исключение, то оно всё равно
бросится, даже если не сможет сконструироваться, правда в этом случае
уже бросится уже несколько другое исключение

> Вообще, sev, вот Вам еще пища для размышлений:

> http://oop.rosweb.ru/users/v/vbelkin/yaace.htm
> Почитайте, думаю Вам будет интересно.

Хм, весьма остроумно Особенно мне понравилась идея с выяснением
типа исключения, пойманного через catch (...). Перечитал стандарт, вроде
ничему не противоречит.

Правда, я не вижу смысла (для себя) решать те проблемы, которые перед
собой поставил автор этой статьи.

С уважением,
Евгений Суходолин
http://www.demoforge.com/
Posted via RSDN NNTP Server 1.4.6 beta
Re[2]: Re: Зачем надо throw new CMyException
От: Аноним  
Дата: 12.03.03 11:50
Оценка:
Здравствуйте, sev, Вы писали:

sev>После некоторых раздумий я реализовал решение, коим весьма теперь доволен.

sev>Все проблемы с автоматическим удалением исключения в нём решены, а также
sev>добавлена возможность "вложенности" исключений в стиле .NET.

sev>Решение можно посмотреть в исходном тексте вот здесь:

sev>http://www.demoforge.com/misc/SmartExceptions.h

sev>Теперь минусы: единственным отрицательным моментом, на мой взгляд, является

sev>то, что для каждого нового класса исключений нужно не забыть сделать один
sev>дополнительный typedef, благодаря которому появляется возможность пользоваться
sev>приведением к базовому классу в catch. В вышеприведённом коде видно, что мы
sev>ловим более абстрактное исключение, чем выбросили.

sev>Любые усовершенствования приветствуются


Мне кажется, что писать что-то вроде
    throw CMyExceptionPtr(new CMyException(...));

довольно неудобно. Вместо этого может имеет смысл сделать специальную функцию
template<class T>
CExceptionPtrT<T, typename T::inherited> wrapped_ptr(T *ptr) {
    return CExceptionPtrT<T, typename T::inherited>(ptr);
}

и использовать ее так
    throw wrapped_ptr(new CMyException(...));

или, даже написать такую
template<class T>
void throw_wrapped_ptr(T *ptr) {
    throw CExceptionPtrT<T, typename T::inherited>(ptr);
}


При этом, конечно, придется делать такой typedef для inherited (базового исключения) в определении исключения, но тогда можно избавиться от того "typedef'а в минусах".

--
Дмитрий
Re[6]: Re[2]: Hey, Hey, Hey
От: jazzer Россия Skype: enerjazzer
Дата: 12.03.03 12:15
Оценка:
Здравствуйте, Ведмедь, Вы писали:

В>Ну не всегда. в некоторых случаях IMHO прозрачней как раз через try/catch ( например, выход из N вложенных циклов )


тут уже был здоровенный флейм на эту тему :)))
Можно сказать, легенда RSDN :)))
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: Зачем надо throw new CMyException
От: grs Россия  
Дата: 12.03.03 12:39
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Здравствуйте, grs, Вы писали:


Нда. Похоже у меня в последнее время туго с выражением собственных мыслей. Так всегда бывает, когда занимаешься 4 делами одновременно.
Короче в том постинге, где ты мне 0 влепил я хотел сказать лишь следующее:
При выкидывании исключения нужно всегда использовать throw CMyExecption, а не throw new CMyExeption, а в блоке нужно писать catch(CMyExeption&), а не catch(CMyExeption) или catch(CMyExeption*). Иначе козленочком станешь. Рано или поздно. Только и всего. Хотя написал коряво, признаю.
Re[2]: Re: Зачем надо throw new CMyException
От: grs Россия  
Дата: 12.03.03 12:43
Оценка:
Здравствуйте, sev, Вы писали:


sev>А ниже показано, как это решение работает "в жизни".


sev>void ThrowingFunc()

sev>{
sev>throw CSystemExceptionPtr(new CSystemException(E_FAIL));
sev>}

sev>Самое главное, что код абсолютно безопасен (я так думаю во всяком случае


А я не понял, а что будет если при вызове ThrowingFunc new выкинет скажем std::bad_alloc? Все-таки не нравится мне эта идея с динамическим выделением памяти при генерации исключения. Проще надо ИМХО, конечно.
Re[14]: Re[2]: Зачем надо throw new CMyException
От: Borisman  
Дата: 12.03.03 12:47
Оценка:
A>Нет, ошибка это ошибка. Функция же может только вернуть результат, который можно трактовать как ошибку.

То, что для одного ошибка — для другого — проза жизни . Это смотря с какой стороны посмотреть. Я лишь говорю о том, что нет единого правила, говорящего о том, следует применять обработку исключений или нет. Но является ли неправильный пароль ИСКЛЮЧИТЕЛЬНОЙ ситуацией? Сколько раз из ста пользователь ошибается при наборе пароля?

A>Ты не аргументировал, почему не следует. Обработка ошибок прямое назначение исключений. Этак половину c++ можно не использовать, мало ли у кого какой здравый смысл.


Не следует использовать средства языка только потому, что они есть. Если нет смысла в применении половины С++ — значит, так тому и быть. Когда Вы в последний раз применяли оператор , (запятая) ?
... << RSDN@Home 1.0 beta 6a >>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.