Re[28]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: drol  
Дата: 08.08.08 07:56
Оценка: -1
Здравствуйте, VoidEx, Вы писали:

VE>Кстати, за это надо руки отрывать, или нет?


Полагаю что надо

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


Нет, основной упор делается на то, что из деструкторов нельзя бросать исключения во время раскрутки стека.

VE>Тогда какие отличия? Только в том, что в шарпе долетает последнее исключение из всей цепочки, а в Си++ — первое.


Неверно. Отличия в том, что в C++ у Вас при раскрутке стека вообще ни одного исключения, а в C# Вы можете без проблем получить любые, что пробрасываются через метод.

VE>Но во-первых в ветке else можно куда-то что-то записать,


Ага-ага. "Глобальные флаги наносят ответный удар 2"...

VE>а во-вторых, из двух исключений, одно из которых вызвало вереницу обвалов, а второе полетело в самом конце из-за того, что файл не закрылся, лично я выбрал бы первое.


А я вот выберу только те исключения, которые меня интересуют.

VE>Но это отдельная тема для дискуссий. В идеале хорошо было бы получить все исключения.


C# предоставляет Вам такую возможность. С++ — нет.

VE>И не пойму, что мешало в шарпе сделать так, чтобы при броске нового исключения предыдущее не затиралось,


Ничего не затирается. То что оппоненты почему-то упорно называют "затиранием", является обычной обработкой исключения по-типу try/catch.

VE>а записывалось новому в Inner автоматически?


Видимо потому что у нового уже может быть свой InnerException. "Новое"-то ведь на самом деле тоже может являться n-ым в своей личной цепи исключений...
Re[29]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: VoidEx  
Дата: 08.08.08 08:47
Оценка: 11 (1) +1
Здравствуйте, drol, Вы писали:

D>Нет, основной упор делается на то, что из деструкторов нельзя бросать исключения во время раскрутки стека.


D>Неверно. Отличия в том, что в C++ у Вас при раскрутке стека вообще ни одного исключения, а в C# Вы можете без проблем получить любые, что пробрасываются через метод.


Вы определитесь.
То только во время раскрутки нельзя кидать, то вообще ни одного...
Я же привёл пример, можно 10 объектов создать, с базовыми, с мемберами. И у всех вызовутся деструкторы, все закроется, а исключения или вылетят, или допишутся в текущее.

D>Ага-ага. "Глобальные флаги наносят ответный удар 2"...


Конечно, способ хранить "текущее" исключение не хорош, ну так исключение может быть любого типа, так что хотя бы.

D>А я вот выберу только те исключения, которые меня интересуют.


И как же? Либо так же оборачивать каждый using в try-catch — или извольте, только последнее.

VE>>Но это отдельная тема для дискуссий. В идеале хорошо было бы получить все исключения.


D>C# предоставляет Вам такую возможность. С++ — нет.


Ни тот, ни другой не предоставляет. В шарпе вы ловите лишь последнее исключение.
Оборачивать каждый пук в try-catch можно везде, но это не метод.

D>Ничего не затирается. То что оппоненты почему-то упорно называют "затиранием", является обычной обработкой исключения по-типу try/catch.


То ли вы поняли, что ошиблись, и теперь виляете туда-сюда, то ли и правда не понимаете.
Вам привели пример, когда долетает только последнее исключение, где предыдущее?
Ответ — "обернуть своим try-catch" аналогичен аругменту за возвращаемые значения — "так надо же возвращаемое значение проверять!". Тогда чем исключение лучше-то?
Исключение тем и хорошо, что всегда попадает в catch на каком-либо уровне и избежать этого нельзя (по идее так должно быть), но вот пример, что делать если исключения сразу два. В шарпе — игнор, в Си++ — terminate. Ни одно решение не универсально.
От того, что вы всё в try-catch обернёте, факт этот никуда не денется.
Получается ещё хуже — не кидать исключения ни в диспозах, ни в финалли, а то вдруг предыдущее затрётся. Как вы тогда получите "то исключение, которое вас интересует"?

Кстати, есть ли аналог std::uncaught_exception?
Re[26]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: Cyberax Марс  
Дата: 08.08.08 08:51
Оценка:
Здравствуйте, drol, Вы писали:

D>Я не знаю как объяснить проще и доступнее. Если Вы не понимаете такой элементарщины, то я Вам ничем помочь больше не могу...

Аналогично. Я считаю, что система должна быть надёжной. И в случае нештатной ситуации корректно завершатся, а не пытаться имитировать, что всё нормально.

C>>Нет. В Java/.NET мы без следов теряем изначальное исключение.

D>Наглая ложь. В Java/C# ничего не теряется. Любое исключение пробрасываемое через функцию может быть поймано и обработано.
Как мне в моём примере из функции main получить RuntimeException, который был заменён NullPointerException'ом?

D>А я говорю про реальное решение. Которое необходимо, бо исключения в деструкторах при раскрутке стека, согласно стандарту, должны нормально обрабатываться независимо от глубины вложенности. Так что не в перезаписи дело, она не является необходимой.

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

C>>Неправда. Мы в блоке finally кидаем исключение, которое ЗАМЕНЯЕТ выброщенный RuntimeException.

D>Чушь. Никто ничего не заменяет. try/finally это по-факту синтаксический сахар на базе try/catch/throw, то есть обычной обработки исключения с его возможным "пробросом". Если Вы обложите try/catch'ем содержимое finally-блока, то на выходе получите свой любимый RuntimeException. А вот теперь попробуйте сделать то же самое с вызовом деструктора автоматического объекта в C++...
Покажи код.

C>>В C++ в этом случае для надёжности решили делать std::terminate.

D>Ну всё, приехали, terminate==надёжность... Даже не знаю что и сказать-то. Разве что порекомендовать почитать БСЭ на тему... раз эдак семь.
Да, это надёжность. Рекомендую прочитать про http://en.wikipedia.org/wiki/Fail-safe . Решение с std::terminate как раз является таковым.

C>>Существует. Выполнение блоков finally — ну абсолютно то же самое. Оно даже внутри реализовано так же.

D>См. выше. try/finally в этом моменте реализован через try/catch. Никакой раскрутки здесь нет.
Да? Ну совсем нет?
void function1()
{
   try
   {
       throw new IllegalStateException();
   } finally
   {
       System.out.println("1");
   }
}

void function2()
{
   try
   {
       function1();
   } finally
   {
       System.out.println("2");
   }
   System.out.println("3");
}


void function3()
{
   try
   {
       function2();
   } finally
   {
       System.out.println("4");
   }
   System.out.println("5");
}


Что будет напечатано при вызове function3() ?
Sapienti sat!
Re: Зачем нужен сборщик мусора? Как жить без деструкторов?
От: Adriano  
Дата: 08.08.08 09:50
Оценка: 1 (1)
Наверно вы все не раз встречали подобный код:

java
myLock.lock();
try {
  //критический фрагмент кода
  ...............
}
finally{
  myLock.unlock();
}

или
Connection conn = getConnection();
try {
  //че-то делаем
  ....
}
finally {
  conn.close();
}


Примеры взял из книжки по яве, которая лежит у меня на столе (Кей. С. Хорстман, Библиотека профессионала. Java 2). Посмотрим теперь плагины из Eclipse — увидим примерно тоже самое: файлы и соединения закрываются без проверки результата. Найдем участки кода из Windows или Linux — то же самое, мьютексы освобождается без проверки! Оказывается существуют случаи, и это абсолютное большинство случаев, когда не нужно проверять результат уничтожения ресурса. И хочу заметить, что Windows, Linux, Eclipse – это очень серьезные проекты, которые созданы далеко не дилетантами. И почему-то мне кажется, что даже drol, котоырй так активно утверждает, что надо проверять все и везде, на самом деле закрывает файл “немой” строкой file.close();

Для этого в с++ существуют деструкторы. Конкретный пример, где в деструкторе выполняется уничтожение обьекта — std::ifstream:
{
  std::ifstream file("data.txt");
  if ( !file ) {
    return false;//файл не открылся
  }
  //работаем с файлом
  .....
  //файл закроется автоматически, при уничтожении обьекта
}

Как видим все очень просто и никакого оверхеда!


Бывают случаи, когда необходимо контролировать уничтожение ресурса и реагировать на различные ошибки. Это встречается гораздо реже, но встречается. В этом случае деструктор — не лучший вариант. Но мы делаем так:

c++
r.open();
try {
  //критический код
  ...
}
catch(...){//либо перехватываем все исключения, либо базовое(std::exception), как надо так и делаем
  //поймано исключение
  ...
}
r.close();//кидаем исключения на право и на лево так, что б аж в глазах потемнело

Все просто и понятно.
Re[23]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: IB Австрия http://rsdn.ru
Дата: 08.08.08 10:33
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Ну выбросит оно предидущее исключение, и что? В С++ так же можно.

Не просто предыдущее, а с полным сохранением стека, то есть, можно проследить оригинальное место возникновения исключения, как буд-то ни какого catch-а не было.

C>Не совсем согласен, что оно существенное.

Нормальный ход — ты утверждаешь, что исключение при перехвате проглатывается, но замечание о том, что, на самом деле, оригинальное исключение в том или ином виде может быть доступно выше по стеку ты находишь не существенным...
Ну, типа, "это противоречит том что я говорил, поэтому это не существенно". =)
... << RSDN@Home 1.2.0 alpha rev. 673>>
Мы уже победили, просто это еще не так заметно...
Re[8]: Зачем нужен сборщик мусора? Как жить без деструкторов
От: Adriano  
Дата: 08.08.08 10:53
Оценка:
Здравствуйте, Пацак, Вы писали:

П>Здравствуйте, Аноним, Вы писали:


А>>c++

А>>
А>>{
А>>  r1.open();
А>>  r2.open();
А>>  r3.open();
А>>  //делаем то, что хотим!!!
А>>  ............
А>>  //метод close автоматически вызовется в деструкторах
А>>}
А>>


А>>В с++ в случаях удовлетворяющих идиоме RAII можно использовать деструкторы, в более сложных случах обработчики завершения, блоки try catch


П>Вообще-то, насколько я понимаю, приведенный выше код RAII как раз не соответствует, т.к. вызов open() происходит явно, а не в конструкторе. А значит (сюрпрайз!) если, например, r2.open() вызван не был, а исключение вылетело где-то на этапе r1.open(), то при выходе из блока в деструкторе r2 все равно будет произведена попытка освобождение никогда не занимавшегося им ресурса. Такая вот "красота" и "читабельность".


Конкретный пример — std::fstream.
Можно так:
{
std::fstream f("1.txt");
...
}

или так:
{
std::fstream f;
f.open("1.txt");
...
}

оба варианта верные
Re[30]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: drol  
Дата: 08.08.08 12:22
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Вы определитесь.

VE>То только во время раскрутки нельзя кидать, то вообще ни одного...

Я уже n раз всё подробно разжёвывал. Могу ещё раз, почти привык уже...

Если нельзя бросать исключения из деструкторов во время раскрутки стека, то по-факту это приводит практически к полному запрету на исключения из деструкторов.
Основания очевидные. Если какой-то деструктор всё-таки бросает исключения, то любая раскрутка стека с автоматическим объектом этого типа и сразу terminate() Остаётся вариант с проверкой uncaught_exception(), однако при нём мы получаем неконсистентное поведение — исключение может быть, а может и не быть...

Посему оппоненты, например, неоднократно постулировали запрет на исключения в деструкторе. И в этом вопросе я с ними согласен, заметьте

VE>Я же привёл пример, можно 10 объектов создать, с базовыми, с мемберами. И у всех вызовутся деструкторы, все закроется, а исключения или вылетят, или допишутся в текущее.


Ваш пример есть полный ужОс.

1. Привязан к кастомному типу, через него нельзя даже просто std::exception() "бросить".
2. Так как во время раскрутки в реальности ничего не бросается, то для Ваших псевдоисключений в деструкторах не работает try/catch. Вам его придётся как-то имитировать ручками, при этом обычный try/catch тоже должен нормально работать.
3. ...

Можно и дальше продолжать, но предлагаю попробовать хотя бы первые два пункта доточить. С интересом погляжу

VE>И как же? Либо так же оборачивать каждый using в try-catch


Да, именно так. Чтобы что-то сделать, надо написать код. Если Вы можете придумать как обрабатывать конкретное исключение без кода, с большим интересом послушаю.

VE>Ни тот, ни другой не предоставляет. В шарпе вы ловите лишь последнее исключение.

VE>Оборачивать каждый пук в try-catch можно везде,

Чушь. В C++ вызов деструктора автоматического объекта обернуть в try-catch невозможно. Если Вы это отрицаете — показывайте пример.

VE>Вам привели пример, когда долетает только последнее исключение, где предыдущее?


Оно было поймано и обработано. Это try/catch, а не "затирание".

VE>Ответ — "обернуть своим try-catch" аналогичен аругменту за возвращаемые значения — "так надо же возвращаемое значение проверять!". Тогда чем исключение лучше-то?


Тем что пробрасывается сквозь стек вызовов, до тех пор пока кто знает что делать не поймает.

VE>Получается ещё хуже — не кидать исключения ни в диспозах, ни в финалли, а то вдруг предыдущее затрётся.


Чушь. Все кидают, только шум стоит. И никаких проблем. Потому что ничего не "затирается", а ловится и обрабатывается.

VE>Кстати, есть ли аналог std::uncaught_exception?


В C# нет раскрутки стека, и код, за исключением finalizer'ов, исполняется в обычном контексте. Мне вот непонятно где можно использовать, даже если и есть... Разве что на уровне MSIL уже...
Re[31]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: VoidEx  
Дата: 08.08.08 12:49
Оценка:
Здравствуйте, drol, Вы писали:

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


D>Основания очевидные. Если какой-то деструктор всё-таки бросает исключения, то любая раскрутка стека с автоматическим объектом этого типа и сразу terminate() Остаётся вариант с проверкой uncaught_exception(), однако при нём мы получаем неконсистентное поведение — исключение может быть, а может и не быть...

В C# мы получаем поведение — исключение вылетит, но может затереть то, которое уже летит.
В C++ долетает только то, что вылетело первым, последующие — не бросаются.
Совпадение и разница видны?

VE>>И как же? Либо так же оборачивать каждый using в try-catch


D>Да, именно так. Чтобы что-то сделать, надо написать код. Если Вы можете придумать как обрабатывать конкретное исключение без кода, с большим интересом послушаю.


Предлагаю заюзать коды возврата и писать код, бо именно это вы и предлагаете.
Толку от исключений, если каждый диспоз надо так обложить.

D>Чушь. В C++ вызов деструктора автоматического объекта обернуть в try-catch невозможно. Если Вы это отрицаете — показывайте пример.


Раз
Автор: Sinclair
Дата: 07.08.08

Два
Автор: VoidEx
Дата: 07.08.08

Только не надо пытаться придраться, что я обрабатываю и конструктор тоже. Сначала сравните код.

VE>>Вам привели пример, когда долетает только последнее исключение, где предыдущее?


D>Оно было поймано и обработано. Это try/catch, а не "затирание".


facepalm
Здесь
Автор: Cyberax
Дата: 08.08.08
приведён код.
Куда улетело RuntimeException? Где его поймали и обработали? И кто?
Ещё
Автор: nikov
Дата: 07.08.08

Код там был такой: using (file1 = ...) using(file2 = ...) using(file3 = ...) { ... }
Каждый файл в Dispose кинул исключение.

D>Чушь. Все кидают, только шум стоит. И никаких проблем. Потому что ничего не "затирается", а ловится и обрабатывается.


Ссылка выше.

D>В C# нет раскрутки стека, и код, за исключением finalizer'ов, исполняется в обычном контексте. Мне вот непонятно где можно использовать, даже если и есть... Разве что на уровне MSIL уже...


Причём тут раскрутка стека. Функция даже называется uncaught_exception.
В C#, как видим, возможна ситуация, когда код выполняется в процессе полёта исключения, можно ли определить в этом самом коде, что в данный момент летит исключение?
Re[23]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: drol  
Дата: 08.08.08 14:36
Оценка:
Здравствуйте, eao197, Вы писали:

D>>Не вижу в этом ничего хорошего. Типичный "Костылиус Вульгарис".

E>Думаю, все еще проще -- вы не понимаете, почему в C++ вызывается terminate(),

Опять с ног на голову. Это оппоненты не понимают, как можно было убедиться по рассказам о "недодеструкченности" и "затирании"...
А лично я отлично понимаю, что комитет принял решение о terminate() совершенно неспроста, и на базе серьёзных оснований. И именно поэтому эти самые основания меня и интересуют...

E>поэтому C++ отстой, а вы весь такой в белом.


Ну причём здесь я-то ??? В белом C#
Re[24]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: Cyberax Марс  
Дата: 08.08.08 18:28
Оценка:
Здравствуйте, IB, Вы писали:

C>>Ну выбросит оно предидущее исключение, и что? В С++ так же можно.

IB>Не просто предыдущее, а с полным сохранением стека, то есть, можно проследить оригинальное место возникновения исключения, как буд-то ни какого catch-а не было.
Да, это преимущество. Хотя, в С++ при выбросе второго исключения тоже можно будет проследить место выброса первого.

По crash-dump'у.

C>>Не совсем согласен, что оно существенное.

IB>Нормальный ход — ты утверждаешь, что исключение при перехвате проглатывается, но замечание о том, что, на самом деле, оригинальное исключение в том или ином виде может быть доступно выше по стеку ты находишь не существенным...
Оно не доступно напрямую (в catch). Но его можно выковырять при желании.

Вот если был бы полноценный catch с учётом вложенных исключений (паттерн-матчинг?), то тогда полностью был бы согласен.
Sapienti sat!
Re[32]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: drol  
Дата: 08.08.08 21:46
Оценка:
Здравствуйте, VoidEx, Вы писали:

D>>Основания очевидные. Если какой-то деструктор всё-таки бросает исключения, то любая раскрутка стека с автоматическим объектом этого типа и сразу terminate() Остаётся вариант с проверкой uncaught_exception(), однако при нём мы получаем неконсистентное поведение — исключение может быть, а может и не быть...

VE>В C# мы получаем поведение — исключение вылетит, но может затереть то, которое уже летит.

Нет никакого "затереть". Всё под контролем программиста. Можно, например, поймать всё что пролетает через функцию, сложить в своё специальное исключение, и на выходе получить вообще все цепочки исключений попавших внутрь функции за период её исполнения, со всеми stacktrace'ами и прочим добром.

И делается это одним движением, и без малейших напрягов. Для C++ же Вы вот уже пробовали написать аналогичный функционал. Как успехи с доточкой ???

VE>В C++ долетает только то, что вылетело первым, последующие — не бросаются.

VE>Совпадение и разница видны?

Конечно видна. В С++ все остальные исключения за пределами деструктора просто отсутствуют, их нет вообще.

VE>Предлагаю заюзать коды возврата и писать код, бо именно это вы и предлагаете.


Не получится. У кода возврата нет stacktrace'а, нет InnerException'а, он не пробрасывается по стеку вызовов, и не попадает в try/catch'и сотоварищи...

VE>Толку от исключений, если каждый диспоз надо так обложить.


См. выше.

D>>Чушь. В C++ вызов деструктора автоматического объекта обернуть в try-catch невозможно. Если Вы это отрицаете — показывайте пример.

VE>Только не надо пытаться придраться, что я обрабатываю и конструктор тоже.

Что значит "не надо" ??? Я просил Вас обернуть вызов деструктора, кузне... тьфу... конструктор мне не нужен.

VE>Сначала сравните код.


Гы А зачем ? Я косяки в постинге Sinclair'а заметил сразу (он сейчас код редко пишет, подотвык уже )

"Но в главном-то он прав" (с) не мой

Смотрите как надо:
    var file1 = new File(...);
    try
    {
        //
    }
    finally
    {
        try
        {
            file1.Dispose();
        }
        catch (Exception ex) { }
    }

Идея понятна ?

VE>>>Вам привели пример, когда долетает только последнее исключение, где предыдущее?

D>>Оно было поймано и обработано. Это try/catch, а не "затирание".
VE>Здесь
Автор: Cyberax
Дата: 08.08.08
приведён код.

VE>Куда улетело RuntimeException? Где его поймали и обработали? И кто?

Его поймала и обработала секция finally. Бо "внизу" она, как и catch, является handler block'ом.

D>>Чушь. Все кидают, только шум стоит. И никаких проблем. Потому что ничего не "затирается", а ловится и обрабатывается.

VE>Ссылка выше.

Вы не поняли. Я говорю о том, что стандартные библиотеки кидают исключения из close() и им подобным.

VE>Причём тут раскрутка стека.


При том, что в C++ понятно где использовать сие сакральное знание. А вот зачем оно в C# ? Там нет этих автоматических ужасов.

VE>В C#, как видим, возможна ситуация, когда код выполняется в процессе полёта исключения,


См. выше. Это эквивалент (почти) catch'а.

VE>можно ли определить в этом самом коде, что в данный момент летит исключение?


Зачем ? Если нужна обработка при исключении — напишите явный catch.
Re[2]: Зачем нужен сборщик мусора? Как жить без деструкторов
От: Кэр  
Дата: 09.08.08 12:16
Оценка:
Здравствуйте, Adriano, Вы писали:

A>
A>Connection conn = getConnection();
A>try {
A>  //че-то делаем
A>  ....
A>}
A>finally {
A>  conn.close();
A>}
A>

A>Примеры взял из книжки по яве, которая лежит у меня на столе (Кей. С. Хорстман, Библиотека профессионала. Java 2). Посмотрим теперь плагины из Eclipse — увидим примерно тоже самое: файлы и соединения закрываются без проверки результата. Найдем участки кода из Windows или Linux — то же самое, мьютексы освобождается без проверки! Оказывается существуют случаи, и это абсолютное большинство случаев, когда не нужно проверять результат уничтожения ресурса. И хочу заметить, что Windows, Linux, Eclipse – это очень серьезные проекты, которые созданы далеко не дилетантами. И почему-то мне кажется, что даже drol, котоырй так активно утверждает, что надо проверять все и везде, на самом деле закрывает файл “немой” строкой file.close();

Паттерн using в C# проверяет объект на равенство нулю перед вызовом Dispose. Все остальные проверки могут быть только семантического рода и к паттернам уже не имеют никакого отношения. В C# есть требование, что вызов Dispose для неоткрытого ресурса не должен вызывать проблем. Точно такое же требование нужно предъявлять и деструкторам в С++, коли уж вам так хочется упихать туда очистку любых ресурсов.
Более того, мне непонятно, а какое премущества, кроме недостатков, мы имеем с передачей объекта ресурса в другой метод по ссылке? Тут уж явно нужно звать delete, который ничем принципиально не отличается от вызова метода Dispose, за исключением отсутствия возможности выбросить исключение и отсутствия поддержки IDE (студия, при наличии R#, начинает волновать, если где-то используется IDisposable объект без using или вызова Dispose).

A>Бывают случаи, когда необходимо контролировать уничтожение ресурса и реагировать на различные ошибки. Это встречается гораздо реже, но встречается. В этом случае деструктор — не лучший вариант.


Это встречаются реже, потому что большая часть деструкторов чистит ресурс "память". Когда чиститься любой другой ресурс — исключение нам нужно всегда. Неважно на каком языке вы пишите.
Если вам нужен надежный клиент — обернете по месту try/catch — и добавите любую логику по вкусу (сразу замечу, что пихать любую логику по вкусу на место проишествия — в деструктор дурная затея).
Но чаще всего совсем каждый вызов оборачивать в try/catch не нужно — достаточно обрамлять большие смысловые блоки. Главное получить exception со всеми подробностями, когда исключительное состояние возникнет. Чтобы сэкономить уйму времени на идентификацию проблемы, когда она возникнет, к примеру, у клиента на Аляске (с клиентом на Аляске вам сильно аукнется "надежный" код, глотающий исключения, при очистке ресурсов).
Re[27]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: drol  
Дата: 09.08.08 14:14
Оценка:
Здравствуйте, Cyberax, Вы писали:

D>>А я говорю про реальное решение. Которое необходимо, бо исключения в деструкторах при раскрутке стека, согласно стандарту, должны нормально обрабатываться независимо от глубины вложенности. Так что не в перезаписи дело, она не является необходимой.

C>Вложенные исключения — вполне очевидно, что работают нормально. Я говорю только про вылетающие за пределы деструктора исключения.

Так вот я и спрашиваю: в чём проблема с остальными ? Почему нельзя делать так же как с исходным исключением, и так же как с обложенными try/catch'ами ?

D>>Чушь. Никто ничего не заменяет. try/finally это по-факту синтаксический сахар на базе try/catch/throw, то есть обычной обработки исключения с его возможным "пробросом". Если Вы обложите try/catch'ем содержимое finally-блока, то на выходе получите свой любимый RuntimeException. А вот теперь попробуйте сделать то же самое с вызовом деструктора автоматического объекта в C++...

C>Покажи код.

Что показать ? Как сделать finally через try/catch/throw ? Например, вот так:
try {
  //
} catch {
  // finally code
  throw;
}

{
  // finally code
}

*Там, конечно, есть тонкости, бо try/catch/обычный блок есть более широкие конструкции, и нужно следить за содержимым // finally code. Чтобы return'ов не было и т.п.

C>>>В C++ в этом случае для надёжности решили делать std::terminate.

D>>Ну всё, приехали, terminate==надёжность... Даже не знаю что и сказать-то. Разве что порекомендовать почитать БСЭ на тему... раз эдак семь.
C>Да, это надёжность. Рекомендую прочитать про http://en.wikipedia.org/wiki/Fail-safe . Решение с std::terminate как раз является таковым.

Гы Спасибо, хоть не русскую wikipedia порекомендовали...

Но тем не менее сгодится. И так смотрим статью, что там нас интересует ? Ага, раздел Electrical or electronic.

The automatic protection of programs and/or processing systems when a hardware or software failure is detected in a computer system. See fail-safe (computer).

Идём по ссылке. Читаем:

In engineering, Fault-tolerant design, also known as fail-safe design, is a design that enables a system to continue operation, possibly at a reduced level (also known as graceful degradation), rather than failing completely, when some part of the system fails.


Дальнейшие комментарии нужны ?

*Учитесь читать...

C>>>Существует. Выполнение блоков finally — ну абсолютно то же самое. Оно даже внутри реализовано так же.

D>>См. выше. try/finally в этом моменте реализован через try/catch. Никакой раскрутки здесь нет.
C>Да? Ну совсем нет?

Да. Совсем нет. Бо раскрутка стека, опять-таки согласно стандарту С++, есть:

The process of calling destructors for automatic objects constructed on the path from a try block to a
throw-expression is called “stack unwinding.”


И, за отсутствием автоматических объектов с деструкторами, "раскрутки" в C# по-определению быть не может. И его спецификация полностью со мной согласна. В ней эти термины не встречаются
*Читайте стандарты, они рулез
Re[24]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 09.08.08 16:02
Оценка:
Здравствуйте, drol, Вы писали:

D>>>Не вижу в этом ничего хорошего. Типичный "Костылиус Вульгарис".

E>>Думаю, все еще проще -- вы не понимаете, почему в C++ вызывается terminate(),

D>Опять с ног на голову. Это оппоненты не понимают, как можно было убедиться по рассказам о "недодеструкченности" и "затирании"...

D>А лично я отлично понимаю, что комитет принял решение о terminate() совершенно неспроста, и на базе серьёзных оснований. И именно поэтому эти самые основания меня и интересуют...

Недодеструкченность таки присутствует:
class A {
  public: ...
    ~A() { if(some_condition) throw some_error(); }
  ...
};
class B {
  public: ...
    ~B() { cleanup_some_important_resource(); }
    ...
};

class C {
  private :
    A m_a;
    B m_b;
  ...
}

Если при разрушении объекта типа C деструктор m_a выбросит исключение, то деструктор m_b не будет вызван. Вот и недодеструкченность.

Чтобы ее избежать, в C++ пришлось бы оборачивать вызов любого деструктора в try..catch, т.е. что-то типа:
void C::__destroy_C_object() {
  // Сначала деструктор C.
  try { this->~C(); } catch( ... ) { ... }
  // Потом деструктор m_a.
  try { this->m_a.~A(); } catch( ... ) { ... }
  // Потом деструктор m_b.
  try { this->m_b.~B(); } catch( ... ) { ... }
}

А теперь попробуйте подсчитать оверхед, который будет нести с собой подобные скобки try/catch на каждый деструктор. И это при том, что в C++ деструкторы вызываются на несколько порядков чаще, чем Dispose в C#.

Это, имхо, одна из возможных причин.
Хотя реальные причины мог бы озвучить, разве что, сам Страуструп.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[25]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: drol  
Дата: 09.08.08 18:05
Оценка:
Здравствуйте, eao197, Вы писали:

E>>>Думаю, все еще проще -- вы не понимаете, почему в C++ вызывается terminate(),


D>>Опять с ног на голову. Это оппоненты не понимают, как можно было убедиться по рассказам о "недодеструкченности" и "затирании"...

D>>А лично я отлично понимаю, что комитет принял решение о terminate() совершенно неспроста, и на базе серьёзных оснований. И именно поэтому эти самые основания меня и интересуют...

E>Недодеструкченность таки присутствует:

E>Если при разрушении объекта типа C деструктор m_a выбросит исключение, то деструктор m_b не будет вызван. Вот и недодеструкченность.

Ещё один "писатель"...

Да нормально всё будет в Вашем примере.

Во-первых, потому что деструктор для m_b вызовется до деструктора m_a. Бо объявлен в классе позже.
А во-вторых, даже если поменять порядок объявлений, то всё равно деструктор для m_b вызовется. Потому как после исключения начнётся раскрутка стека, и в ней, разумеется, будет вызов этого самого деструктора.

"Недодеструкченность" же о которой шла речь, это неисполнение кода внутри деструктора; кода оставшегося "за пределами" после возникновения исключения.

*Прежде чем постить примерчеги, рекомендуется их позапускать хотя бы пару раз... Помогает избежать

E>Чтобы ее избежать, в C++ пришлось бы оборачивать вызов любого деструктора в try..catch, т.е. что-то типа:


Так вот оно и "оборачивается". Есть таблицы вызовов деструкторов автоматических объектов/данных-членов. Только механизма работает всего лишь до второго исключения из деструктора почему-то О чём и вопрос, собственно...

E>Хотя реальные причины мог бы озвучить, разве что, сам Страуструп.


Не знаю насчёт БС, но вот Вам точно не светит их когда-либо озвучить...
Re[25]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: VoidEx  
Дата: 09.08.08 18:56
Оценка:
Здравствуйте, eao197, Вы писали:

E>Недодеструкченность таки присутствует:


Можно подтверждение из стандарта? А то в 9-й студии вызываются и базовые деструкторы и деструкторы мемберов без каких-либо проблем. Останавливается только деструктор самого объекта.
Re[33]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: VoidEx  
Дата: 09.08.08 19:32
Оценка:
Здравствуйте, drol, Вы писали:

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


D>Нет никакого "затереть". Всё под контролем программиста. Можно, например, поймать всё что пролетает через функцию, сложить в своё специальное исключение, и на выходе получить вообще все цепочки исключений попавших внутрь функции за период её исполнения, со всеми stacktrace'ами и прочим добром.


...

D>И делается это одним движением, и без малейших напрягов. Для C++ же Вы вот уже пробовали написать аналогичный функционал. Как успехи с доточкой ???


В моём "примере", извините, всё делалось автоматом, без расстановки новых try-catch. Если это несущественно, то и говорить не о чем.

D>Конечно видна. В С++ все остальные исключения за пределами деструктора просто отсутствуют, их нет вообще.


...

D>"Но в главном-то он прав" (с) не мой


D>Смотрите как надо:

D>
D>    var file1 = new File(...);
D>    try
D>    {
D>        //
D>    }
D>    finally
D>    {
D>        try
D>        {
D>            file1.Dispose();
D>        }
D>        catch (Exception ex) { }
D>    }
D>

D>Идея понятна ?

Да. Автоматизм где? Ручками все умеют.
А два файла? Два try-catch внутри finally?

file f;
try
{
}
catch ( ... )
{
}
f.close();


D>Его поймала и обработала секция finally. Бо "внизу" она, как и catch, является handler block'ом.


Стоп.

try
{
  f1(); // throws
}
finally
{
  // Тут как понять, что летит исключение?
}


Если внутри finally сделать throw, то полетит новое исключение, если не сделать — то оригинальное (которое вылетело из f1()).
Соответственно, если обламалась попытка освободить ресурс — швыряем исключение, при этом старое пропадает. Но так как у вас "все на совести программиста", вы предлагаете писать так (для сохранения оригинального исключения):


try
{
  f1(); // throws
}
catch // try throwed
{
  // cleanup without throw
  throw; // rethrow original exception
}
// try not throwed
// cleanup with throw


Э? И чем это лучше вот этого:

my::~my()
{
  // cleanup
  if (!std::uncaught_exception())
    throw ...;
}


Я имею такие же основания утверждать, что в Си++ всё на совести программиста, и если вы хотите затереть(вычеркнуто) обработать старое исключение, то пишите так:

file f; // destructor may throw (only if !std::uncaught_exception)
try
{
}
catch ( ... )
{
  f.close(); // throws new exception
}
// destructor throws new exception


Если вы всё ещё не видите, что в C# и C++ есть краткозаписывамое решение "по умолчанию" (затирать старое исключение и не кидать нового соответственно) и есть способ сделать наоборот ручками (причем способы аналогичны), но продолжать дискуссию не имеет смысла.

VE>>Причём тут раскрутка стека.


D>При том, что в C++ понятно где использовать сие сакральное знание. А вот зачем оно в C# ? Там нет этих автоматических ужасов.


При автоматическом освобождении там есть ужас затирания исключения.
Выход — обкладывать все try-catch'ами или писать как в вашем примере (ручками звать Dispose).
Оба метода применимы и в Си++.

D>Зачем ? Если нужна обработка при исключении — напишите явный catch.


void foo()
{
  // летит ли сейчас исклчюение? т.е. если я брошу - затру ли я его?
}


На место комментария вставьте, пожалуйста, код, который определит мне, летит ли исключение.
Re[34]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: VoidEx  
Дата: 09.08.08 19:36
Оценка:
Здравствуйте, VoidEx, Вы писали:

Важный момент, finally сам за меня решает, как обработать летящее исключение, — это просто проигнорировать. Си++ этого не делает, потому игнорировать (не кидать новое исключение при уже летящем) решает сам программист.
И то, и другое — два разных решения, а не "C++ в говне, а C# в белом".
Re[34]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: drol  
Дата: 09.08.08 22:19
Оценка: -4
Здравствуйте, VoidEx, Вы писали:

D>>И делается это одним движением, и без малейших напрягов. Для C++ же Вы вот уже пробовали написать аналогичный функционал. Как успехи с доточкой ???


VE>В моём "примере", извините, всё делалось автоматом, без расстановки новых try-catch. Если это несущественно, то и говорить не о чем.


Гы В Вашем примере try/catch вообще не работает. Автоматчик... Так что либо дотачивайте, либо "слив засчитан"

VE>Да. Автоматизм где?


Передёргивать не надо, уважаемый Речь не о выдуманном Вами каком-то там "автоматизме", а о try/catch'е вокруг вызова деструктора автоматического объекта.

VE>Ручками все умеют.


Не умеете. А если считаете что умеете, тогда кончайте вилять, и давайте показывайте обёртку вызова деструктора автоматического объекта try/catch'ем, иначе "слив засчитан 2"

VE>А два файла? Два try-catch внутри finally?


Разумеется, нет. На каждый ресурс должен быть свой try/finally. Не нужно в кучу мешать...

VE>
VE>file f;
VE>try
VE>{
VE>}
VE>catch ( ... )
VE>{
VE>}
VE>f.close();
VE>

Это вообще к чему и про что

VE>Если внутри finally сделать throw, то полетит новое исключение, если не сделать — то оригинальное (которое вылетело из f1()).

VE>Соответственно, если обламалась попытка освободить ресурс — швыряем исключение, при этом старое пропадает. Но так как у вас "все на совести программиста", вы предлагаете писать так (для сохранения оригинального исключения):

Ничего не понял Вы хотите пробросить из finally исходное исключение ? Ну так я же выше показал как это делать: оборачиваете внутренности finally в try/catch.

VE>Э? И чем это лучше вот этого:


Не знаю. Я не понимаю, зачем Вы оба этих куска кода написали. Тезис какой ? Чего хотите ?

VE>Я имею такие же основания утверждать, что в Си++ всё на совести программиста, и если вы хотите затереть(вычеркнуто) обработать старое исключение, то пишите так:


Не имеете. Бо исключение из деструктора при раскрутке стека => terminate() В схеме же с try/finally из Java/C# проблем такого рода нет.

VE>
VE>file f; // destructor may throw (only if !std::uncaught_exception)
VE>try
VE>{
VE>}
VE>catch ( ... )
VE>{
VE>  f.close(); // throws new exception
VE>}
VE>// destructor throws new exception
VE>

Какой ещё close() ??? Ваш тезис — освобождение ресурса в деструкторе. Вот и освобождайте там. А close() это мой тезис, а не Ваш.

D>>При том, что в C++ понятно где использовать сие сакральное знание. А вот зачем оно в C# ? Там нет этих автоматических ужасов.


VE>При автоматическом освобождении там есть ужас затирания исключения.


Нет никакого "затирания". Есть пойманное и обработанное finally исключение.

VE>Выход — обкладывать все try-catch'ами или писать как в вашем примере (ручками звать Dispose).


Если требуется более сложная логика обработки исключений — она будет реализована именно так.

VE>Оба метода применимы и в Си++.


Гы В случае использования аналога IDisposable/try/finally, конечно применимы!!! В случае же очистки ресурса в деструкторе — обломс.

VE>
VE>void foo()
VE>{
VE>  // летит ли сейчас исклчюение? т.е. если я брошу - затру ли я его?
VE>}
VE>

Опять двадцать пять. Да не затирает никто, ничто, никого и ничего. Исключения сохраняются на стеке. А потом ловятся где-то там наверху.

Ну зачем Вашей foo() знать, в каких условиях произошёл вызов ??? Это не её проблемы. Кто вызывает, тот и заботится о том, нужно ему исключение или нет. Для того исключения и придумали, чтобы передвинуть обработку от точки их возникновения, да на тот уровень стека вызовов, который знает что делать в этом случае. А Вы опять пытаетесь засунуть всё обратно в самый низ...
*И эти люди пишут на C++...
Re[35]: Зачем нужен сборщик мусора? Как жить без деструкторо
От: VoidEx  
Дата: 10.08.08 02:29
Оценка: +2
Здравствуйте, drol, Вы писали:

Извините, но продолжать с вами разговор я больше не хочу.
Упирайтесь рогом перед зеркалом и хамите туда же.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.