Re[2]: try-catch
От: alexkro  
Дата: 06.05.04 06:36
Оценка:
Здравствуйте, LaptevVV, Вы писали:

Feedback, понимаешь!

...
LVV>Общая схема обработки исключений такова: в одной части программы, где обнаружена аварийная ситуация, исключение генерируется; другая часть программы, которая следит за возникновением

"следит" как-то не звучит. Создается впечатление, что какая-то отдельная сущьность там обитает и "следит".

>исключения, ловит и обрабатывает его. В С++ есть три зарезервированных слова: try (контролировать), catch (ловить), throw (порождать), — которые и используются для организации процесса обработки исключений.


throw — это скорее выбрасывать. Отсюда и "ловить".

LVV>Генерация исключений


...
LVV>Программист может и сам определить свой собственный тип объекта-исключения, объявив новый класс, например
LVV>
LVV>class NegativeArgument{};
LVV>NegativeArgument exeption;
LVV>if (x>0) double t = x/sqrt(x);
LVV>else throw exeption;
LVV>


Если кто попытается это откомпилировать, у него могут быть проблемы с std::exception.

LVV>Пустые классы неожиданно пригодились! Однако объявлять переменную совсем не обязательно, можно


Очень много неявного контекста. У непосвящённого "пустые классы пригодились" вызовет лишь недоумение.

>обойтись и без этого, например

LVV>
LVV>throw NegativeArgument();
LVV>


LVV>Здесь в качестве "выражения генерации исключения" мы использовали явный вызов конструктора без аргументов, и это наиболее часто используемая форма генерации исключения.

LVV>Объект-исключение может быть и динамическим, например,
LVV>
LVV>throw new NegativeArgument();
LVV>


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


Опять-же ненужное усложнение. Это деталь уводит в второну от освещения основного предмета. Советую перенести в ссылку, а то и вовсе удалить.

LVV>Перехват и обработка исключений


...
LVV>Тип должен быть одним из допустимых типов исключений — либо встроенный, либо определенный программистом. Первый вариант спецификации означает, что объект-исключение передается в блок обработки, чтобы там его как-то использовать, например, для вывода информации в сообщении об ошибке. При этом объект-исключение может передаваться в секцию-ловушку любым способом: по значению, по ссылке или по указателю, например
LVV>
LVV>catch (TException e)            // по значению
LVV>catch (TException& e)            // по ссылке
LVV>catch (const TException& e)        // по константной ссылке
LVV>catch (TException *e)            // по по указателю
LVV>


Желательно выдать рекомендацию по использованию catch(TException & e). Не обязательно прямо здесь, но где-нибудь в примере.

...
LVV>Работа конструкции try-catch напоминает работу оператора switch. Секции-ловушки похожи на альтернативы case, а catch-блок с многоточием соответствует альтернативе default оператора-переключателя. Если в try-блоке возникает исключение, то начинается сравнение типа сгенерированного исключения и типов параметров в секциях-ловушках. Выполняется тот catch-блок, тип параметра которого совпал с типом исключения. Если такого типа не найдено, но есть catch с многоточием, то выполняется его блок. Если же такого блока нет, то вызывается стандартная функция завершения terminate().

std::terminate()

LVV>Таким образом, очень важен порядок записи секций-ловушек после контролируемого блока. Если в качестве первого обработчика после try-блока задан catch-блок с параметром-многоточием, то такой обработчик будет обрабатывать все возникающие исключения. До остальных секций-ловушек дело просто не дойдет. Поэтому усвойте следующее простое правило: всегда задавать catch-блок с параметром-многоточием последним.


Следует все-таки упамянуть, что catch(...) — вреден. Хорошая практика — никогда не использовать его в своих примерах.

LVV>Выход из секции-ловушки выполняется одним из следующих способов:

LVV>1. выполнились все операторы обработчика — происходит неявный переход на оператор, расположенный после конструкции try-catch;
LVV>2. в обработчике выполняется оператор goto; разрешается выполнять оператор на любой оператор вне конструкции try-catch; внутрь контролируемого блока и в другую секцию ловушку переход запрещен;

Это очень интересно, но лучше опустить или в ссылку вставить. Потому как либо описание претендует на полноту — тогда нужно все детали приводить, либо нет — тогда лучше опустить детали отвлекающие от основного предмета.

LVV>3. в секции ловушке выполняется оператор throw;

LVV>4. в обработчике генерируется другое исключение;
LVV>После выполнения операторов catch-блока при отсутствии явных операторов перехода или оператора throw выполняются операторы, расположенные после всей конструкции try-catch. Если во время работы в try-блоке не обнаружено исключительной ситуации, то все catch-блоки пропускаются, и программа продолжает выполнение с первого оператора после всех catch.
LVV>Интересно, что можно объявлять контролируемым блок, являющийся телом функции. Например, мы можем контролировать все исключения, возникающие в теле главной функции, написав такую конструкцию:
LVV>
LVV>int main()
LVV>try { // контролируемый блок – тело функции
LVV>      // …
LVV>    }
LVV>catch(...)
LVV>{ // сообщения об исключениях
LVV>}
LVV>


Зачем это может кому-то понадобиться? Function try blocks имеют смысл только в конструкторах.

LVV>Если в контролируемом блоке не возникнет исключений, то блок функции нормально завершается. Если же исключения возникнут, то, как обычно, выполняется секция-ловушка, и только после этого программа завершается. Секций-ловушек, естественно, может быть прописано столько, сколько необходимо.

LVV>В стандарте [1] приводится следующий пример на ту же тему (листинг 4.1).
LVV>
LVV>//Листинг 4.1. Контролируемый блок — тело функции
LVV>int f(int);                    // функция, определенная в другом месте
LVV>class C
LVV>{ int i; double d;                // поля
LVV>  public:
LVV>    C(int, double);             // конструктор инициализации
LVV>// …
LVV>};
LVV>C::C(int ii, double id)            // реализация конструктора 
LVV>try                                // контролируем
LVV>:i(f(ii)), d(id)                // список инициализации
LVV>{   // …                        // тело конструктора
LVV>}
LVV>catch(…)
LVV>{     // …                            // обработчик исключений
LVV>}
LVV>


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


Ну плохой пример, плохой! Function try blocks лучше вообще не затрагивать, ибо в таком ограниченном объеме нормально описать его идею, не вызвав полнейшее смятение читателя, невозможно.

LVV>Блоки try-catch могут быть вложенными, причем как в try-блок, так и в catch-блок:

LVV>
LVV>try {    // блок, который может инициировать исключения
LVV>    try {     //вложенный блок
LVV>    }
LVV>    catch(…){    }
LVV>}
LVV>catch (исключение) {    // обработка исключения
LVV>    try {     //вложенный блок
LVV>    }
LVV>    catch(…){    }
LVV>} 
LVV>


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


А почему эта важная фраза в середине/конце, а не в начале?

LVV>При обработке исключения в секции-ловушке можно сгенерировать повторное исключение того же типа — это делается оператором throw без выражения генерации исключения. Такая форма допустима только внутри секции-ловушки.


Везде можно imho. Пример:
void foobar()
{
    throw;
}

void bar()
{
    try {
        throw Exception();
    }
    catch ( Exception & ) {
        foobar();
    }
}

void foo()
{
    try {
        bar();
    }
    catch ( Exception & ) {
    }
}

>Однако это не приводит к рекурсивному входу в тот же обработчик — ищется другой обработчик выше по иерархии вложенности. Если такого обработчика нет, то программа аварийно завершается. Таким образом, мы имеем возможность "распределить" обработку исключения по разным частям программы.
Re[3]: try-catch
От: LaptevVV Россия  
Дата: 06.05.04 06:47
Оценка:
Здравствуйте, alexkro, Вы писали:

LVV>>Общая схема обработки исключений такова: в одной части программы, где обнаружена аварийная ситуация, исключение генерируется; другая часть программы, которая следит за возникновением

A>"следит" как-то не звучит. Создается впечатление, что какая-то отдельная сущьность там обитает и "следит".

>>исключения, ловит и обрабатывает его. В С++ есть три зарезервированных слова: try (контролировать), catch (ловить), throw (порождать), — которые и используются для организации процесса обработки исключений.


A>throw — это скорее выбрасывать. Отсюда и "ловить".

Спасибо! Добавля пояснения термина.
A>...
LVV>>Программист может и сам определить свой собственный тип объекта-исключения, объявив новый класс, например
LVV>>
LVV>>class NegativeArgument{};
LVV>>NegativeArgument exeption;
LVV>>if (x>0) double t = x/sqrt(x);
LVV>>else throw exeption;
LVV>>

A>Если кто попытается это откомпилировать, у него могут быть проблемы с std::exception.
Спасибо — сделаю примечание!
LVV>>Пустые классы неожиданно пригодились! Однако объявлять переменную совсем не обязательно, можно
Это из предыдущих глав — книжка-то для моих второкурсников. А это 4-я глава.
A>Очень много неявного контекста. У непосвящённого "пустые классы пригодились" вызовет лишь недоумение.

>>обойтись и без этого, например

LVV>>
LVV>>throw NegativeArgument();
LVV>>


LVV>>Здесь в качестве "выражения генерации исключения" мы использовали явный вызов конструктора без аргументов, и это наиболее часто используемая форма генерации исключения.

LVV>>Объект-исключение может быть и динамическим, например,
LVV>>
LVV>>throw new NegativeArgument();
LVV>>


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


A>Опять-же ненужное усложнение. Это деталь уводит в второну от освещения основного предмета. Советую перенести в ссылку, а то и вовсе удалить.

Подумаю, спасибо!
LVV>>Перехват и обработка исключений

A>...

LVV>>Тип должен быть одним из допустимых типов исключений — либо встроенный, либо определенный программистом. Первый вариант спецификации означает, что объект-исключение передается в блок обработки, чтобы там его как-то использовать, например, для вывода информации в сообщении об ошибке. При этом объект-исключение может передаваться в секцию-ловушку любым способом: по значению, по ссылке или по указателю, например
LVV>>
LVV>>catch (TException e)            // по значению
LVV>>catch (TException& e)            // по ссылке
LVV>>catch (const TException& e)        // по константной ссылке
LVV>>catch (TException *e)            // по по указателю
LVV>>


A>Желательно выдать рекомендацию по использованию catch(TException & e). Не обязательно прямо здесь, но где-нибудь в примере.

Спасибо!
A>...
LVV>>Работа конструкции try-catch напоминает работу оператора switch. Секции-ловушки похожи на альтернативы case, а catch-блок с многоточием соответствует альтернативе default оператора-переключателя. Если в try-блоке возникает исключение, то начинается сравнение типа сгенерированного исключения и типов параметров в секциях-ловушках. Выполняется тот catch-блок, тип параметра которого совпал с типом исключения. Если такого типа не найдено, но есть catch с многоточием, то выполняется его блок. Если же такого блока нет, то вызывается стандартная функция завершения terminate().

A>std::terminate()

Ну, еще раз! Хотя до std мы еще не добрались.
LVV>>Таким образом, очень важен порядок записи секций-ловушек после контролируемого блока. Если в качестве первого обработчика после try-блока задан catch-блок с параметром-многоточием, то такой обработчик будет обрабатывать все возникающие исключения. До остальных секций-ловушек дело просто не дойдет. Поэтому усвойте следующее простое правило: всегда задавать catch-блок с параметром-многоточием последним.

A>Следует все-таки упамянуть, что catch(...) — вреден. Хорошая практика — никогда не использовать его в своих примерах.

Да, наверное. Подумаю, как сформулировать.
LVV>>Выход из секции-ловушки выполняется одним из следующих способов:
LVV>>1. выполнились все операторы обработчика — происходит неявный переход на оператор, расположенный после конструкции try-catch;
LVV>>2. в обработчике выполняется оператор goto; разрешается выполнять оператор на любой оператор вне конструкции try-catch; внутрь контролируемого блока и в другую секцию ловушку переход запрещен;

A>Это очень интересно, но лучше опустить или в ссылку вставить. Потому как либо описание претендует на полноту — тогда нужно все детали приводить, либо нет — тогда лучше опустить детали отвлекающие от основного предмета.

Здесь вроде все на месте и нечего добавить. Просто определение.
LVV>>3. в секции ловушке выполняется оператор throw;
LVV>>4. в обработчике генерируется другое исключение;
LVV>>После выполнения операторов catch-блока при отсутствии явных операторов перехода или оператора throw выполняются операторы, расположенные после всей конструкции try-catch. Если во время работы в try-блоке не обнаружено исключительной ситуации, то все catch-блоки пропускаются, и программа продолжает выполнение с первого оператора после всех catch.
LVV>>Интересно, что можно объявлять контролируемым блок, являющийся телом функции. Например, мы можем контролировать все исключения, возникающие в теле главной функции, написав такую конструкцию:
LVV>>
LVV>>int main()
LVV>>try { // контролируемый блок – тело функции
LVV>>      // …
LVV>>    }
LVV>>catch(...)
LVV>>{ // сообщения об исключениях
LVV>>}
LVV>>


A>Зачем это может кому-то понадобиться? Function try blocks имеют смысл только в конструкторах.

Возможно усилить в сторону конструкторов именно! Что мол, в конструкторах — очередная фича... которая может применяться и в других местах, если вдруг приспичит... Ы?
LVV>>Если в контролируемом блоке не возникнет исключений, то блок функции нормально завершается. Если же исключения возникнут, то, как обычно, выполняется секция-ловушка, и только после этого программа завершается. Секций-ловушек, естественно, может быть прописано столько, сколько необходимо.
LVV>>В стандарте [1] приводится следующий пример на ту же тему (листинг 4.1).
LVV>>
LVV>>//Листинг 4.1. Контролируемый блок — тело функции
LVV>>int f(int);                    // функция, определенная в другом месте
LVV>>class C
LVV>>{ int i; double d;                // поля
LVV>>  public:
LVV>>    C(int, double);             // конструктор инициализации
LVV>>// …
LVV>>};
LVV>>C::C(int ii, double id)            // реализация конструктора 
LVV>>try                                // контролируем
LVV>>:i(f(ii)), d(id)                // список инициализации
LVV>>{   // …                        // тело конструктора
LVV>>}
LVV>>catch(…)
LVV>>{     // …                            // обработчик исключений
LVV>>}
LVV>>


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


A>Ну плохой пример, плохой! Function try blocks лучше вообще не затрагивать, ибо в таком ограниченном объеме нормально описать его идею, не вызвав полнейшее смятение читателя, невозможно.

Ну — это прямо из стандарта.
А какова идея. на ваш взгляд?

LVV>>Блоки try-catch могут быть вложенными, причем как в try-блок, так и в catch-блок:

LVV>>
LVV>>try {    // блок, который может инициировать исключения
LVV>>    try {     //вложенный блок
LVV>>    }
LVV>>    catch(…){    }
LVV>>}
LVV>>catch (исключение) {    // обработка исключения
LVV>>    try {     //вложенный блок
LVV>>    }
LVV>>    catch(…){    }
LVV>>} 
LVV>>


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


A>А почему эта важная фраза в середине/конце, а не в начале?

Не знаю, так получилось. На ваш взгляд — где?
LVV>>При обработке исключения в секции-ловушке можно сгенерировать повторное исключение того же типа — это делается оператором throw без выражения генерации исключения. Такая форма допустима только внутри секции-ловушки.

A>Везде можно imho. Пример:

A>
A>void foobar()
A>{
A>    throw;
A>}

A>void bar()
A>{
A>    try {
A>        throw Exception();
A>    }
A>    catch ( Exception & ) {
A>        foobar();
A>    }
A>}

A>void foo()
A>{
A>    try {
A>        bar();
A>    }
A>    catch ( Exception & ) {
A>    }
A>}

>>Однако это не приводит к рекурсивному входу в тот же обработчик — ищется другой обработчик выше по иерархии вложенности. Если такого обработчика нет, то программа аварийно завершается. Таким образом, мы имеем возможность "распределить" обработку исключения по разным частям программы.

Спасибо за пример. Что значит фраза "везде можно имхо" ? Мое имхо или ваше?
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[4]: try-catch
От: alexkro  
Дата: 06.05.04 07:15
Оценка:
Здравствуйте, LaptevVV, Вы писали:

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


A>>Следует все-таки упамянуть, что catch(...) — вреден. Хорошая практика — никогда не использовать его в своих примерах.

LVV>Да, наверное. Подумаю, как сформулировать.

Мне все-таки нравится другой подход: вырабатывать у читателя вкус к хорошему стилю программирования путём приведения грамотных примеров. В данном случае простое упоминание, что мол catch(...) вреден, с последующим его использованием в большинстве примеров как-то не стыкуется, правда? Лучше, я думаю, будет сказать "catch(...) — зло" *и* подкрепить это дело путём объявления класса Exception в каком-нибудь из первых примеров, а потом только его и ловить. А лучше в последствии уточнить класс Exception, унаследовав его от std::exception, и начать ловить std::exception.

LVV>>>2. в обработчике выполняется оператор goto; разрешается выполнять оператор на любой оператор вне конструкции try-catch; внутрь контролируемого блока и в другую секцию ловушку переход запрещен;


A>>Это очень интересно, но лучше опустить или в ссылку вставить. Потому как либо описание претендует на полноту — тогда нужно все детали приводить, либо нет — тогда лучше опустить детали отвлекающие от основного предмета.

LVV>Здесь вроде все на месте и нечего добавить. Просто определение.

Может быть. Но данный случай (goto из catch блока) настолько редок, что я его почти никогда ни в чьём коде и не видел.

LVV>>>
LVV>>>int main()
LVV>>>try { // контролируемый блок – тело функции
LVV>>>      // …
LVV>>>    }
LVV>>>catch(...)
LVV>>>{ // сообщения об исключениях
LVV>>>}
LVV>>>


A>>Зачем это может кому-то понадобиться? Function try blocks имеют смысл только в конструкторах.

LVV>Возможно усилить в сторону конструкторов именно! Что мол, в конструкторах — очередная фича... которая может применяться и в других местах, если вдруг приспичит... Ы?

Тогда отдельную главу по этому поводу для продвинутых читателей, и в приложение её!

LVV>>>В стандарте [1] приводится следующий пример на ту же тему (листинг 4.1).

LVV>>>
LVV>>>//Листинг 4.1. Контролируемый блок — тело функции
LVV>>>int f(int);                    // функция, определенная в другом месте
LVV>>>class C
LVV>>>{ int i; double d;                // поля
LVV>>>  public:
LVV>>>    C(int, double);             // конструктор инициализации
LVV>>>// …
LVV>>>};
LVV>>>C::C(int ii, double id)            // реализация конструктора 
LVV>>>try                                // контролируем
LVV>>>:i(f(ii)), d(id)                // список инициализации
LVV>>>{   // …                        // тело конструктора
LVV>>>}
LVV>>>catch(…)
LVV>>>{     // …                            // обработчик исключений
LVV>>>}
LVV>>>


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


A>>Ну плохой пример, плохой! Function try blocks лучше вообще не затрагивать, ибо в таком ограниченном объеме нормально описать его идею, не вызвав полнейшее смятение читателя, невозможно.

LVV>Ну — это прямо из стандарта.
LVV>А какова идея. на ваш взгляд?

Идея в том, что исключения в конструкторах — это отдельная и довольно непростая тема, которую в паре параграфов не объяснишь. Там много тонких моментов, которые требуют совсем не поверхностного знания языка. Тех, кто только начинает изучать исключения такая тема только выбьет из колеи.

LVV>>>Блоки try-catch могут быть вложенными, причем как в try-блок, так и в catch-блок:

LVV>>>
LVV>>>try {    // блок, который может инициировать исключения
LVV>>>    try {     //вложенный блок
LVV>>>    }
LVV>>>    catch(…){    }

Вот здесь, например, почему бы не написать catch(Exception&)?

LVV>>>}
LVV>>>catch (исключение) {    // обработка исключения
LVV>>>    try {     //вложенный блок
LVV>>>    }
LVV>>>    catch(…){    }
LVV>>>} 
LVV>>>


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


A>>А почему эта важная фраза в середине/конце, а не в начале?

LVV>Не знаю, так получилось. На ваш взгляд — где?

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

LVV>>>При обработке исключения в секции-ловушке можно сгенерировать повторное исключение того же типа — это делается оператором throw без выражения генерации исключения. Такая форма допустима только внутри секции-ловушки.


A>>Везде можно imho. Пример:

A>>
A>>void foobar()
A>>{
A>>    throw;
A>>}

A>>void bar()
A>>{
A>>    try {
A>>        throw Exception();
A>>    }
A>>    catch ( Exception & ) {
A>>        foobar();
A>>    }
A>>}

A>>void foo()
A>>{
A>>    try {
A>>        bar();
A>>    }
A>>    catch ( Exception & ) {
A>>    }
A>>}

>>>Однако это не приводит к рекурсивному входу в тот же обработчик — ищется другой обработчик выше по иерархии вложенности. Если такого обработчика нет, то программа аварийно завершается. Таким образом, мы имеем возможность "распределить" обработку исключения по разным частям программы.

LVV>Спасибо за пример. Что значит фраза "везде можно имхо" ? Мое имхо или ваше?


Я так понял из текста, что нигде throw; нельзя кроме как непосредственно в catch. Так вот можно и вне catch.
Re[5]: try-catch
От: LaptevVV Россия  
Дата: 06.05.04 07:29
Оценка:
Здравствуйте, alexkro, Вы писали:

A>>>Следует все-таки упамянуть, что catch(...) — вреден. Хорошая практика — никогда не использовать его в своих примерах.

LVV>>Да, наверное. Подумаю, как сформулировать.

A>Мне все-таки нравится другой подход: вырабатывать у читателя вкус к хорошему стилю программирования путём приведения грамотных примеров. В данном случае простое упоминание, что мол catch(...) вреден, с последующим его использованием в большинстве примеров как-то не стыкуется, правда? Лучше, я думаю, будет сказать "catch(...) — зло" *и* подкрепить это дело путём объявления класса Exception в каком-нибудь из первых примеров, а потом только его и ловить. А лучше в последствии уточнить класс Exception, унаследовав его от std::exception, и начать ловить std::exception.

Я думаю, это позже будет, когда разработку реального приложения буду показывать

LVV>>>>2. в обработчике выполняется оператор goto; разрешается выполнять оператор на любой оператор вне конструкции try-catch; внутрь контролируемого блока и в другую секцию ловушку переход запрещен;


A>>>Это очень интересно, но лучше опустить или в ссылку вставить. Потому как либо описание претендует на полноту — тогда нужно все детали приводить, либо нет — тогда лучше опустить детали отвлекающие от основного предмета.

LVV>>Здесь вроде все на месте и нечего добавить. Просто определение.
A>Может быть. Но данный случай (goto из catch блока) настолько редок, что я его почти никогда ни в чьём коде и не видел.
ИМХО как раз начинающие-то и пытаются использовать goto — сам такой
A>Тогда отдельную главу по этому поводу для продвинутых читателей, и в приложение её!
Ну, подумаю.
LVV>>>>В стандарте [1] приводится следующий пример на ту же тему (листинг 4.1).
A>>>Ну плохой пример, плохой! Function try blocks лучше вообще не затрагивать, ибо в таком ограниченном объеме нормально описать его идею, не вызвав полнейшее смятение читателя, невозможно.
LVV>>Ну — это прямо из стандарта.
LVV>>А какова идея. на ваш взгляд?

A>Идея в том, что исключения в конструкторах — это отдельная и довольно непростая тема, которую в паре параграфов не объяснишь. Там много тонких моментов, которые требуют совсем не поверхностного знания языка. Тех, кто только начинает изучать исключения такая тема только выбьет из колеи.

Да, это у Саттера ОЧЕНЬ подробно написано. Возможно, действительно, для первого знакомства не стоит об этом писать.

A>Вот здесь, например, почему бы не написать catch(Exception&)?

Спасибо.
LVV>>>>}
LVV>>>>catch (исключение) { // обработка исключения
LVV>>>> try { //вложенный блок
LVV>>>> }
LVV>>>> catch(…){ }
LVV>>>>}
LVV>>>>[/ccode]

A>Ну это-же основное предназначение исключений. С этого начинать нужно, по-моему.

Да, посмотрю начало еще раз.

LVV>>Спасибо за пример. Что значит фраза "везде можно имхо" ? Мое имхо или ваше?


A>Я так понял из текста, что нигде throw; нельзя кроме как непосредственно в catch. Так вот можно и вне catch.

Ну, вроде это приведет к аварийному завершению? Я чего-то в стандарте этот момент пропустил?


Кстати, почему дальше-то никто не читает? там еще три поста — с примерами. И как раз с объяснениями кодов возврата и прочее...
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[2]: try-catch
От: alexkro  
Дата: 06.05.04 07:56
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>
LVV>//Листинг 4.3 Функция вычисления высоты треугольника с генерацией исключений
LVV>double Ha(double a, double b, double c)
LVV>{ if ((a>0)&&(b>0)&&(c>0))                    // если параметры правильные 
LVV>  { if ((a+b>c)&&(a+c>b)&&(b+c>a))             // если треугольник
LVV>    { double p = (a+b+c)/2;                    // вычисление высоты
LVV>      return 2*sqrt(p*(p-a)*(p-b)*(p-c))/a;  
LVV>    }
LVV>    else throw "Не треугольник!";            // генерируем исключение
LVV>  }
LVV>  else throw "Неправильный параметр!";        // генерация исключения
LVV>}


Я бы переписал:
double Ha(double a, double b, double c)
{
    if (a <= 0 || b <= 0 || c <= 0)
        throw exception("Неправельный параметр!");
    if (a + b <= c || a + c <= b || b + c <= a)
        throw exception("Не треугольник!");
    double p = (a + b + c) / 2;
    return 2 * sqrt(p * (p - a) * (p - b) * (p - c)) / a;
}


Несколько моментов: форматирование неудачное. Функция становится более ясной, если сначала отбросить все ошибочные ситуации, а потом заниматься основной логикой. Это более близко к "промышленному" стилю программирования (опять-же возможность научить студента хорошему стилю).

LVV>Обратите внимание на то, что в функции не выполняется возврат "аварийного значения" 0 — в данном случае этого не требуется, так как оператор генерации исключения осуществляет выход из функции. При


Если программу написать немножко по-другому, то эти вещи даже и объяснять не прийдется.

>этом выполняется обычный процесс уничтожения локальных объектов, и вызываются все необходимые деструкторы. Этот процесс уничтожения локальных объектов при выходе по исключению называется "раскруткой" стека. Поэтому вернуться в функцию после генерации исключения невозможно — разве что заново ее вызвать.


LVV>Наша функция не имеет спецификации исключений. Наличие спецификации в заголовке, например,

LVV>
LVV>double Ha(double a, double b, double c) throw(const char *) 
LVV>

LVV>никак не изменяет поведения программы.
LVV>Если мы напишем спецификацию исключения с типом исключения, не соответствующим типу генерируемого, то программа должна закончится аварийно. Однако не все системы поддерживают это требование стандарта. В частности, Visual C++.NET 2003 задание спецификация с другим типом исключения, например,
LVV>
LVV>double Ha(double a, double b, double c) throw(string) 
LVV>


А зачем объяснять такую возможность языка, которая ни одним компилятором не реализуется, нигде не используется и вообще считается сломанной?

LVV>или вовсе запрещающая исключения

LVV>
LVV>double Ha(double a, double b, double c) throw() 
LVV>

LVV>никак не влияет на видимое поведение программы. Главное, чтобы генерируемое исключение было где-нибудь перехвачено.

А вот тут просто ошибка. throw() очень хорошо поддерживатся компиляторами и является единственно важным случаем спецификации исключений. Нужно отдельно объяснить этот случай, делая ударение на то, что если объявить throw(), а потом все-таки выбросить, то случится плохое.

LVV>Передача информации в блок обработки


LVV>
LVV>  } 
LVV>  catch(const ErrorTriangle& e)                // получаем полноценный объект
LVV>  { cout << e.message << endl;                // используем поля
LVV>    cout << e.a <<' '<< e.b <<' '<< e.c << endl;
LVV>  }
LVV>}
LVV>


catch( const ...) не несёт никаких дополнительных возможностей. Просто: catch(ErrorTriangle & e).
Re[6]: try-catch
От: jazzer Россия Skype: enerjazzer
Дата: 06.05.04 08:04
Оценка:
Здравствуйте, LaptevVV, Вы писали:

A>>Я так понял из текста, что нигде throw; нельзя кроме как непосредственно в catch. Так вот можно и вне catch.

LVV>Ну, вроде это приведет к аварийному завершению? Я чего-то в стандарте этот момент пропустил?

Пропустил.
Eго можно использовать где угодно.
Если в момент использования у нас есть обрабатываемое исключение, оно перевыбросится.
Если нет, позовется terminate.
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[3]: try-catch
От: jazzer Россия Skype: enerjazzer
Дата: 06.05.04 08:11
Оценка: 20 (1)
Здравствуйте, alexkro, Вы писали:


A>Следует все-таки упамянуть, что catch(...) — вреден. Хорошая практика — никогда не использовать его в своих примерах.


почему это он вреден?

вот пример его использования (интересно, как тут обойтись без него?):
try {
   f();
}
catch (const MyException& e)
{
   ...
}
catch (const HisException& e)
{
   ...
}
catch (const TheirException& e)
{
   ...
}
catch (const std::exception& e)
{
   ...
}
catch (...) // случилась какая-то фигня и хз что с ней делать, может, еще кто знает?
{
   cerr << "случилась какая-то фигня и хз что с ней делать, на всякий случай все чистим, роллбэчим, логгим и т.п.\n";
   ... // чистим, роллбэчим, логгим и т.п.
   throw; // теперь пусть другие пытаются сделать что-то более осмысленное
}
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[4]: try-catch
От: alexkro  
Дата: 06.05.04 08:40
Оценка: 6 (1)
Здравствуйте, jazzer, Вы писали:

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



A>>Следует все-таки упамянуть, что catch(...) — вреден. Хорошая практика — никогда не использовать его в своих примерах.


J>почему это он вреден?


J>вот пример его использования (интересно, как тут обойтись без него?):

J>
J>try {
J>   f();
J>}
J>catch (const MyException& e)
J>{
J>   ...
J>}
J>catch (const HisException& e)
J>{
J>   ...
J>}
J>catch (const TheirException& e)
J>{
J>   ...
J>}
J>catch (const std::exception& e)
J>{
J>   ...
J>}
J>catch (...) // случилась какая-то фигня и хз что с ней делать, может, еще кто знает?
J>{
J>   cerr << "случилась какая-то фигня и хз что с ней делать, на всякий случай все чистим, роллбэчим, логгим и т.п.\n";
J>   ... // чистим, роллбэчим, логгим и т.п.
J>   throw; // теперь пусть другие пытаются сделать что-то более осмысленное
J>}

J>

Мы здесь студентов учим, понимаешь! Шутка .

А кроме шуток, теоретичеки код можно писать вообще без него, а практически с одним catch(...) в самом конце main(), в котором выполняется операция die fast.

Допустим, все твои исключения унаследованны от std::exception. Как же ты можешь получить ...? А вот так, например: Access Violation поймался в catch(...), и при этом не вызвались деструкторы и твоя программа в неопределенном состоянии! Ты попытался сделать rollback, закорраптил данные и записал их на диск поверх корректных, но не до конца — во время записи случился еще один AV, и теперь ты в луже!

Поэтому лучше различать аппаратные исключения и программные (унаследованные от std::exception желательно). Для программных — catch(std::exception&), а для аппаратных — __try/__except (под win32) или что предлагает тебе твоя любимая платформа. И если поймал аппаратное — die fast, man! die fast.
Re[3]: try-catch
От: jazzer Россия Skype: enerjazzer
Дата: 06.05.04 08:52
Оценка:
Здравствуйте, alexkro, Вы писали:

LVV>>Передача информации в блок обработки


LVV>>
LVV>>  } 
LVV>>  catch(const ErrorTriangle& e)                // получаем полноценный объект
LVV>>  { cout << e.message << endl;                // используем поля
LVV>>    cout << e.a <<' '<< e.b <<' '<< e.c << endl;
LVV>>  }
LVV>>}
LVV>>


A>catch( const ...) не несёт никаких дополнительных возможностей. Просто: catch(ErrorTriangle & e).


Как и в любой другой ситуации, const несет не дополнительные возможности, а дополнительные ограничения :)

Если ты не собираешься изменять объект исключения — пиши с const.
Если собираешься (скажем, чтобы дописать в него какую-то информацию и после перевыбросить) — пиши без const.
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[3]: try-catch
От: LaptevVV Россия  
Дата: 06.05.04 09:04
Оценка:
Здравствуйте, alexkro, Вы писали:

A>Я бы переписал:

A>
A>double Ha(double a, double b, double c)
A>{
A>    if (a <= 0 || b <= 0 || c <= 0)
A>        throw exception("Неправельный параметр!");
A>    if (a + b <= c || a + c <= b || b + c <= a)
A>        throw exception("Не треугольник!");
A>    double p = (a + b + c) / 2;
A>    return 2 * sqrt(p * (p - a) * (p - b) * (p - c)) / a;
A>}
A>

Выглядит действительно посимпатичнее. Спасибо!
A>Функция становится более ясной, если сначала отбросить все ошибочные ситуации, а потом заниматься основной логикой. Это более близко к "промышленному" стилю программирования (опять-же возможность научить студента хорошему стилю).
А мне всегда казадлось, что сначала надо выполнить основную работу, а всякие исключения из правил — потом.
LVV>>Обратите внимание на то, что в функции не выполняется возврат "аварийного значения" 0 — в данном случае этого не требуется, так как оператор генерации исключения осуществляет выход из функции. При

A>Если программу написать немножко по-другому, то эти вещи даже и объяснять не прийдется.

Ну, у меня там есть пример "неправильного" применения — я его сюда не дал — и так много. Эта фраза — от того примера.

A>А зачем объяснять такую возможность языка, которая ни одним компилятором не реализуется, нигде не используется и вообще считается сломанной?

Во всех книжках по С++ она все равно описывается.
LVV>>или вовсе запрещающая исключения
LVV>>
LVV>>double Ha(double a, double b, double c) throw() 
LVV>>

LVV>>никак не влияет на видимое поведение программы. Главное, чтобы генерируемое исключение было где-нибудь перехвачено.

A>А вот тут просто ошибка. throw() очень хорошо поддерживатся компиляторами и является единственно важным случаем спецификации исключений. Нужно отдельно объяснить этот случай, делая ударение на то, что если объявить throw(), а потом все-таки выбросить, то случится плохое.

Еще раз проверю. У меня от VC осталось именно такое впечатление — да и в helpe написано явно, что VC не поддерживает. И unexpected нужно явно вызывать. И даже пример приводится.
LVV>>Передача информации в блок обработки

LVV>>
LVV>>  } 
LVV>>  catch(const ErrorTriangle& e)                // получаем полноценный объект
LVV>>  { cout << e.message << endl;                // используем поля
LVV>>    cout << e.a <<' '<< e.b <<' '<< e.c << endl;
LVV>>  }
LVV>>}
LVV>>


A>catch( const ...) не несёт никаких дополнительных возможностей. Просто: catch(ErrorTriangle & e).

Привычка. Вместо передачи по значению всегда пишу по константной ссылке.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[5]: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 06.05.04 09:49
Оценка:
Здравствуйте, alexkro, Вы писали:

A>>Никакого знания здесь нет, throw неявно вызывает функцию _CxxThrowException которая вызывает RaiseException

A>>Где там JMP
A>На ia64 imho там простейший jump.

А что, вызов деструкторов Hardware Supported?
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[5]: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 06.05.04 10:00
Оценка: +1
Здравствуйте, alexkro, Вы писали:

A>Допустим, все твои исключения унаследованны от std::exception. Как же ты можешь получить ...?


Используя стороннюю библиотеку.

A>Поэтому лучше различать аппаратные исключения и программные (унаследованные от std::exception желательно).


catch (unsigned int hardwareExceptionCode)

поможет
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: try-catch
От: alexkro  
Дата: 08.05.04 08:05
Оценка:
Здравствуйте, LaptevVV, Вы писали:

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


A>>А вот тут просто ошибка. throw() очень хорошо поддерживатся компиляторами и является единственно важным случаем спецификации исключений. Нужно отдельно объяснить этот случай, делая ударение на то, что если объявить throw(), а потом все-таки выбросить, то случится плохое.

LVV>Еще раз проверю. У меня от VC осталось именно такое впечатление — да и в helpe написано явно, что VC не поддерживает. И unexpected нужно явно вызывать. И даже пример приводится.

throw() — это особый случай. По крайней мере VC++ (не знаю как на счёт остальных компиляторов) никакого кода по проверке выбрасываемых исключений не генерирует. Напротив:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_langref_nothrow.asp

Что это означает? Компилятор может соптимизировать exception frames в случае, если он видит, что ни одна из вызываемых функций не выбрасывает исключения. Поэтому если исключение все-таки будет выбрашено, то правильной раскрутки стека просто не произойдёт.
Re[2]: try-catch
От: Alexey Chen Чили  
Дата: 08.05.04 11:10
Оценка:
Здравствуйте, jazzer, Вы писали:

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


B>>Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch..


J>Проблема в том, что такой код легко превращается в абсолютно нечитабельный код типа


[...]

J>а мог бы быть таким


J>
J>try
J>{
J>   f1();
J>   f2();
J>   f3();
J>   f4();
J>   f5();
J>}
J>catch(int status)
J>{
J>   report_status(status);
J>   kill them all;
J>}
J>


J>какой вариант понятнее, проще и читабельнее?


Нда... а таким он мог бы быть?

#define chkerr(x) do { if ( (x) != ERR_SUCCESS ) { ReportErrorAt(__FILE__,__LINE__,#x); goto lb_error;} } while(0)
/// здесь описаны явно удаляемые обьекты
{
   /// здесь описанны локальные обьекты, например умные указатели
   chkerr( f1() );
   chkerr( f2() );
   chkerr( f3() );
   chherr( f4() );
   chkerr( f5() );
   goto lb_end;
}
lb_error:
   /// обработка ошибочной ситуации
lb_end:
   /// делаем явное удаление обьектов



А еще могло бы быть так
  if ( !f1()
    || !f2()
    || !f3()
    || !f4()
    || !f5() )
  {
    // обработка ошибки
  }


Это я не к тому, что исключения не надо использовать, а к тому, что приведенный пример не являестся основанием для их использования. Да и пример весьма вырожденный.


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

try
{
//....
}
catch(...)
{
//kill all
throw;
}
// kill all

Без владения RAII, использовать исключения весьма и весьма непросто. А RAII вещь более нетривиальная, чем исключения.
Re[3]: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 08.05.04 12:19
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

Просвятите пожалуйста, что такое RAII
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: try-catch
От: jazzer Россия Skype: enerjazzer
Дата: 08.05.04 12:41
Оценка:
Здравствуйте, adontz, Вы писали:

A>Здравствуйте, Alexey Chen, Вы писали:


A>Просвятите пожалуйста, что такое RAII


выделение ресурсов есть инициализация — когда все выделения ресурсов обертываются в соответствующие классы
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[3]: try-catch
От: jazzer Россия Skype: enerjazzer
Дата: 08.05.04 12:48
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

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


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


B>>>Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch..


J>>Проблема в том, что такой код легко превращается в абсолютно нечитабельный код типа


AC>[...]


J>>а мог бы быть таким


J>>
J>>try
J>>{
J>>   f1();
J>>   f2();
J>>   f3();
J>>   f4();
J>>   f5();
J>>}
J>>catch(int status)
J>>{
J>>   report_status(status);
J>>   kill them all;
J>>}
J>>


J>>какой вариант понятнее, проще и читабельнее?


AC>Нда... а таким он мог бы быть?


AC>
AC>#define chkerr(x) do { if ( (x) != ERR_SUCCESS ) { ReportErrorAt(__FILE__,__LINE__,#x); goto lb_error;} } while(0)
AC>/// здесь описаны явно удаляемые обьекты
AC>{
AC>   /// здесь описанны локальные обьекты, например умные указатели
AC>   chkerr( f1() );
AC>   chkerr( f2() );
AC>   chkerr( f3() );
AC>   chherr( f4() );
AC>   chkerr( f5() );
AC>   goto lb_end;
AC>}
AC>lb_error:
AC>   /// обработка ошибочной ситуации
AC>lb_end:
AC>   /// делаем явное удаление обьектов
AC>


ага
макросы и goto — это самое оно
а ничего, что коды возврата у разных функций могут быть разными?
Т.е. одни возвращают int, другие — bool, остальные — свои сотвственные енумы?
Как бы выглядела твоя функция?

AC>А еще могло бы быть так

AC>
AC>  if ( !f1()
AC>    || !f2()
AC>    || !f3()
AC>    || !f4()
AC>    || !f5() )
AC>  {
AC>    // обработка ошибки
AC>  }
AC>


Это только если в качестве статуса возвращается bool или нечто к нему приводимое.
Ситуация малополезная.
В том же COM, насколько я помню, отвал не равен нулю.

AC>Это я не к тому, что исключения не надо использовать, а к тому, что приведенный пример не являестся основанием для их использования. Да и пример весьма вырожденный.


Совершенно не вырожденный, если ты напишешь соответствующие осмысленные имена функций.
В скольких проектах я работал — такие ситуации на каждом шагу.
В том же MFC есть цепочка типа OnPreparePrinting, OnBeginPrinting etc

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


AC>try

AC>{
AC> //....
AC>}
AC>catch(...)
AC>{
AC> //kill all
AC> throw;
AC>}
AC>// kill all

AC>Без владения RAII, использовать исключения весьма и весьма непросто.


Во-первых, приведенный выше код сравнительно прост, а во-вторых, у тебя будет тот же самый геморрой и с кодами возврата.

AC> А RAII вещь более нетривиальная, чем исключения.


Имхо, более тривиальная, только надо умеючи каскадные вещи обрабатывать.
Тем не менее к исключениям это имеет небольшое отношение.
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[4]: try-catch
От: Alexey Chen Чили  
Дата: 08.05.04 13:23
Оценка:
Здравствуйте, jazzer, Вы писали:

J>ага

J>макросы и goto — это самое оно :)
А собственно чего плохого в макросах и goto?
Что они противоречат идее (религии) идеальной программы?
Дык, если разговор о сферических конях в вакуме, то я пожалй пойду от сюда. Не силен я в софофилии.

J>а ничего, что коды возврата у разных функций могут быть разными?

J>Т.е. одни возвращают int, другие — bool, остальные — свои сотвственные енумы?
J>Как бы выглядела твоя функция?

В конкретном примере, который я назвал вырожденым, ловится инт, а для остальных ошибок происходит утечка ресурсов.
Смысл бросать инт?

Если меня не поняли, то я указал на то, что данный пример не является причиной использовать исключения (или не использовать).

J>Это только если в качестве статуса возвращается bool или нечто к нему приводимое.

J>Ситуация малополезная.
J>В том же COM, насколько я помню, отвал не равен нулю.

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

J>В том же MFC есть цепочка типа OnPreparePrinting, OnBeginPrinting etc

И что из этого следует? Что лучший способ обработки ошибок — это отлов исключений? Имхо, монописсуально.

AC>>Использование механизма исключений требует не только знание самого механизма, но и активное использование RAII там где он не AC>>Без владения RAII, использовать исключения весьма и весьма непросто.


J>Во-первых, приведенный выше код сравнительно прост, а во-вторых, у тебя будет тот же самый геморрой и с кодами возврата.

О чем я собственно и сказал, и исключения там, как козе баян.

AC>> А RAII вещь более нетривиальная, чем исключения.


J>Имхо, более тривиальная, только надо умеючи каскадные вещи обрабатывать.

J>Тем не менее к исключениям это имеет небольшое отношение.

Еще надо умеючи писать врапперы для вызова функций API/библиотек который ничего о RAII не знают.
Что уже к исключениям имеет самое прямое отношение, так как finally в C++ не предусмотрен.
Имхо, лишняя работа которая обычно нафиг не нужна.
Re[5]: try-catch
От: WolfHound  
Дата: 09.05.04 11:53
Оценка: 6 (1)
Здравствуйте, Alexey Suda-Chen aka hedgehog, Вы писали:
Знакомые все лица...

AC>А собственно чего плохого в макросах и goto?

Поддержка, читабельность и надежность летят коту под хвост...
AC>Что они противоречат идее (религии) идеальной программы?
Нет но их использование должно быть оочень окуратным.

J>>Во-первых, приведенный выше код сравнительно прост, а во-вторых, у тебя будет тот же самый геморрой и с кодами возврата.

AC>О чем я собственно и сказал, и исключения там, как козе баян.
(/* [in] */                        DWORD dwCount
,/* [size_is][in] */            OPCITEMDEF *pItemArray
,/* [size_is][size_is][out] */    OPCITEMRESULT **ppAddResults
,/* [size_is][size_is][out] */    HRESULT **ppErrors
)
try
{
    scoped_lock(this);//лочим текущий объект//макрос
    com_check(owner_);//если проверка провалилась кидаем исключение
    com_check_ptr(pItemArray);
    com_check_ptr(ppAddResults);
    com_check_ptr(ppErrors);
    com_check_arg(dwCount>0);
    com_auto_arr<OPCITEMRESULT>    ir(dwCount);//выделяем память при помощи CoTaskMemAlloc//если не смогли кидаем исключение.
    com_auto_arr<HRESULT>        hr(dwCount);
    HRESULT res=S_OK;
    for(size_t i=0;i<dwCount;++i)
    {
        ref_t<CSRCOMM_OPCGroupItem> item=new CSRCOMM_OPCGroupItem(this);
        OPCHANDLE handle=get_new_handle();
        hr[i]=item->init(&pItemArray[i], &ir[i], handle);
        if(FAILED(hr[i]))
        {
            res=S_FALSE;
            ir[i].dwAccessRights        =0;
            ir[i].dwBlobSize            =0;
            ir[i].hServer                =-1;
            ir[i].pBlob                    =0;
            ir[i].vtCanonicalDataType    =VT_ERROR;
            ir[i].wReserved                =0;
        }
        else
            item_map_[handle]=item;
    }

//возвращаем результаты работы//исключений нет
    *ppAddResults    =ir.detach();
    *ppErrors        =hr.detach();
    return res;
}
com_catch_all()//расшифровываем исключение//макрос

Для сравнения код из аналогичного сервака (не соответствующего стандарту и вобще постоянно падующего и память у него течет) найдено в инте
    DWORD            dwNumItems,
    OPCITEMDEF     * pItemArray,
    OPCITEMRESULT ** ppAddResults,
    HRESULT       ** ppErrors
    )
{
    unsigned int    i;
    OPCITEMRESULT *ir;
    HRESULT *hr;
    SYSTEMTIME SystemTime;
    BOOL    ok = TRUE;
    COPCItem    *pItem;

    GetApp()->DisplayEvent ("IOPCItemMgt::AddItems");

    // First - allocate memory for the result array(s)
    //
    ir = (OPCITEMRESULT*)GetApp()->pIMalloc->Alloc(sizeof(OPCITEMRESULT) * dwNumItems);    //acc001
    if(ir == NULL)
        {
        *ppAddResults = NULL;
        *ppErrors = NULL;
        return (E_OUTOFMEMORY);
        }

    *ppAddResults = ir;

    hr = (HRESULT*) GetApp()->pIMalloc->Alloc(sizeof(HRESULT) *dwNumItems);        //acc001
    if (hr == NULL)
        {
        GetApp()->pIMalloc->Free(ir);    
        *ppAddResults = NULL;
        *ppErrors = NULL;
        return (E_OUTOFMEMORY);
        }

    *ppErrors = hr;

    // Now for each item... 
    //
    for(i=0; i<dwNumItems; i++)
        {
        hr[i] = GetApp()->ValidateItem (&pItemArray[i], &ir[i]);

        if (hr[i] == S_OK)
            {
            // Create a new OPCItem
            pItem = new (COPCItem);

            pItem->Name = pItemArray[i].szItemID;
            pItem->AccessPath = pItemArray[i].szAccessPath;
            pItem->IsActive = pItemArray[i].bActive;
            pItem->NativeDataType = VT_R4;
            pItem->RequestedDataType = pItemArray[i].vtRequestedDataType;
            pItem->Value = 0.0;
            pItem->Quality = OPC_QUALITY_UNCERTAIN;
            GetSystemTime(&SystemTime);        // Get current UTC Time
            SystemTimeToFileTime(&SystemTime, &(pItem->TimeStamp)); // and store it
            pItem->ClientHandle = pItemArray[i].hClient;
            // Server Handle will be assigned by Group
            pGroup->AddItem(pItem);

            // Set ITEM RESULT
            ir->hServer = pItem->SvrHandle;
            ir->vtCanonicalDataType = pItem->NativeDataType;
            ir->dwAccessRights = OPC_WRITEABLE | OPC_READABLE;
            ir->pBlob = NULL;
            ir->dwBlobSize = 0;
            }

        }

    for (i=0; i<dwNumItems; i++)
        {
        if (hr[i] != S_OK)
            return (E_FAIL);    // Some Items could not be added
        }
    
    return (S_OK);

}

Какой код понятние? А какой надежние? А какой править легче?
AC>Еще надо умеючи писать врапперы для вызова функций API/библиотек который ничего о RAII не знают.
Эта проблема надумана. Враперы пишутся раз и навсегда. И ни чего сложного в их описании нет.
AC>Что уже к исключениям имеет самое прямое отношение, так как finally в C++ не предусмотрен.
И не надо.
AC>Имхо, лишняя работа которая обычно нафиг не нужна.
Практика показывает что даже если врапер испльзуется только один раз то даже в этом случае нужна ибо позволяет разнести основной алгоритм и логику работы с ресурсом что повышает как читабельность алгоритма так и логики работы с конкретным ресурсом.
... << RSDN@Home 1.1.3 beta 1 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[6]: try-catch
От: Alexey Chen Чили  
Дата: 09.05.04 14:55
Оценка: :)
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, Alexey Suda-Chen aka hedgehog, Вы писали:

WH>Знакомые все лица...
Вроде набрудершафт не пили.

AC>>А собственно чего плохого в макросах и goto?

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

J>>>Во-первых, приведенный выше код сравнительно прост, а во-вторых, у тебя будет тот же самый геморрой и с кодами возврата.

AC>>О чем я собственно и сказал, и исключения там, как козе баян.

[пример поскипан]
Противопоставить заведомо хороший и плохой вариант и сказать, что плохой плох именно по тому, что он чего-то не использует :)

Не понял, что ты хотел доказать? Что хорошо написанный код, лучше плохо написанного? ИМХО, это докозательства не требует.
Что без RAII и исключений хороший код писать нельзя или писать его сложнее? Ну, это просто пробел в твоем образовании.

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

Еще раз. Я никогда не говорил, что исключения — зло, я говорил, что они не всегда нужны, как и RAII.
И использовать исключения без RAII практически невозможно, но можно использовать RAII без исключений там, где оно естественно.
Нпример, захват COM обьекта.

Лично мне монописсуально какой способ обработки ошибок использовать, я писал и так и эток, и повсякому можно сделать просто, понятно и надежно.

P.S.
Удивительно, но люди часто забывают, что C++ — всего лишь инструмент, и решают не поставленую задачу, а задачу — решения поставленной задачи способом наиболее соответствующем тому который-используют-в-своих-книгах-крутые-мужики.
Если в программе нет шаблонов и исключений — это С с классами. Язык пионэров? Мачто на нем непушут, да? :)
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.