try-catch
От: Blitz Великобритания  
Дата: 02.05.04 00:04
Оценка:
Здравствуйте в 225 или какой-там раз =) Спасибо большое за все предыдущие ответы, я очень благодарна вам всем, да и RSDN тоже.

////////////////////////СПАСИБО!!!!!/////////////////

Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...

Все эти try — catch... больше всего пугает throw...

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

В общем, я уже читала и Шилдта и Страуструпа и STL C++ Reference (microsoft_овскую) и...

НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch???

что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???


ПОЖАААААААААААЛАААААСТАААААА!!!!!
Re: try-catch
От: MaximE Великобритания  
Дата: 02.05.04 01:39
Оценка:
Здравствуйте, Blitz, Вы писали:

B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...


B>Все эти try — catch... больше всего пугает throw...


[]

http://www.google.com/search?hl=en&ie=UTF-8&oe=UTF-8&q=exception+handling+c%2B%2B&spell=1
Re[2]: try-catch
От: Blitz Великобритания  
Дата: 02.05.04 03:00
Оценка: -1
Здравствуйте, MaximE, Вы писали:

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


B>>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...


B>>Все эти try — catch... больше всего пугает throw...


ME>[]


ME>http://www.google.com/search?hl=en&ie=UTF-8&oe=UTF-8&q=exception+handling+c%2B%2B&spell=1


не смешно.

неужели не понятно, что мне надо "для идиотов" объснение. Если бы и так понятно было....
Re: try-catch
От: Аноним  
Дата: 02.05.04 05:00
Оценка:
Тсс... Не кричи так на весь RSDN!

Exception handling удобен тогда, когда нужно разделить reporting of errors and their handling. Обычно тот, кто пишет функцию может обнаружить ошибку, но не знает, что делать в таком случае. Тот, кто использует эту функцию, знает, что нужно сделать в случае ошибки но не может ее обнаружить (так как не он писал код). Это из Страуструпа.

Например, мы работаем над игрой, и я пишу класс для загрузки 3D моделей. Ты используешь мой класс, и в таком случае я должен тебе как-то дать знать, что произошла ошибка, чтобы ты смогла (как-то корректно) продолжить программу. Можно сообщать об ошибке через глобальную переменную, но ты знаешь, что код почти невозможно читать если их и так в программе какое-то большое количество. Можно вернуть ошибочное значение из функции, но это не всегда удобно, так как трудно порой подобрать значение функции, которое бы указывало на ошибку. Из конструктора класса, например, невозможно вернуть значение вообще!

Итак, я решаю использовать исключения, и мой код выглядит примерно так:
class C3DModel
{
private:
      // Some private data here:
      ...

public:
      class CFileNotFoundException
      {
      private:
            const char *FileThatWasNotFound;

      public:
            CFileNotFoundException(const char *FileName) : FileThatWasNotFound(FileName) { }
            const char *WhichFile() const { return FileThatWasNotFound; }
      };

public:
      // Constructor:
      C3DModel(const char *FileName = NULL)
      {
            // Делаем здесь что-то:
            ...

            // Пытаемся открыть файл и если файл не найден, то "кидаем" исключение:
            int ret = ::TryToOpenFile(FileName);
            if (ret == ERROR)
                    throw CFileNotFoundException(FileName); // Дадим пользователю знать какой файл мы
                                                            // не смогли найти. ТАКЖЕ, БУДЬ ОСТОРОЖНА С
                                                            // throw В КОНСТРУКТОРЕ! МОЖЕТ ПРОИЗОЙТИ RESOURCE
                                                            // LEAK!!!
            // Делаем здесь еще что-то если все в порядке:
            ...
      }

      ~C3DModel() { ... }

      void Load3DModelFile(const char *FileName)
      {
            // Код аналогичен коду в конструкторе...
      }

      void Animate3DModel() const { ... }
};


Твой код будет выглядеть примерно так:
int main()
{
      try {
            C3DModel MyMonster("UglyMonster.3dm");
            MyMonster.Animate3DModel();  // Исполняется если файл был открыт успешно
      }
      catch (const C3DModel::CFileNotFoundException &e) {
            // Эта ветка исполнится если мы не смогли загрузить монстра...

            // Сообщим об ошибке!
            cerr << "Failed to load file " << e.WhichFile() << endl;

            // Сделаем еще что-нибудь тут:
            ...

            return 1;
      }

      return 0;
}
Re: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 02.05.04 05:09
Оценка: 3 (1) +1 :))
Здравствуйте, Blitz, Вы писали:

B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...

Бывает
B>Все эти try — catch... больше всего пугает throw...
Да, особенно это w на конце напоминает усики!
B>Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch..
Иногда это дейтсвительно легче, но иобработку исключений тоже лучше знать.
B>В общем, я уже читала и Шилдта и Страуструпа и STL C++ Reference (microsoft_овскую) и...
Я не думаю, что я дорос до их уровня и авторитета, но попытка не пытка
B>НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch???
Постараюсь.
B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???

Для начала, что такое try/catch? Это попробовать и если случилась ошибка поймать её.
try
 {
  // КАКОЙ-ТО КОД, В КОТОРОМ МОЖЕТ СЛУЧИТСЯ ОШИБКА
 }
catch(...)
 {
  // ЛОВИМ ВСЕ ОШИБКИ
 }

Казалось бы такой код эквивалентен следующему
if (не ошибки)
 {
  // ЧТО-ТО СДЕЛАТЬ
 }
else
 {
  // ПРИБРАТЬ ЗА СОБОЙ
 }

Но это только кажется. В чём же разница?

Механизм исключений позволяет отлавливать ошибки место возникновения которых заранее не известно.

Говоря другими слова, когда ты пишешь try, тебе не известно (или всё равно) где именно возникнет ошибка. Главное, что где-то внутри этого try. А если писать с помошью if, то надо самому отследить все возможные места возникновения ошибкок. Вот пример который нельзя переписать с помошью if
void func()
 {
  throw 0; // Собственно сгенерировали исключение.
 }
////////////
void func2()
 {
  try
   {
    func();
   }
  catch
   {
   }
 }

Исключение возникшее в func будет всё равно отловленно.
Далее надо заметить, что кроме программных ошибок (не смогли открыть файл и проч), существуют и аппаратные ошибки (обратились по несуществующему или чужому адресу в памяти). Вот простейщий пример
int * p = NULL;
*p = 3;

В данном случае будет попытка разыменовать указатель который не ссылается на переменную типа int. Это ошибка. Такая ошибка вызовет исключение.

Механизм исключенйи позволяет отсеивать "чужие" ошибки

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

Мы отлавливаем все исключения, но когда пишем так
catch(int)
///////////////////////////////
catch(CMyException, int, float)

Мы явно указываем исключения какого типа хотим отлавливать.

Расмотрим пример. Пусть есть класс для обработки группы чисел и класс-хранилище для этих чисел.
Класс хранилище умеет, при нехватке оперативной памяти часть данных сбрасывать на жествкий диск. Но это его внутреннее устройство. Соответственно внутри хранилища есть 2 вида ошибок: не удалось выделить память и не удалось создать файл.
Внутри класса обработки есть два вида ошибок: не удалось загрузить файл и неправильный формат данных.
Вот схематично try/catch этих классов

////////////////////////
Загрузить и обработать данные.
////////////////////////
void DoAllWork()
 {
  try
   {
    LoadData();
   }
  catch(LoaderError)
   {
    throw CanNotLoad;
   }
  Process();
 }
///////////////////////
Загрузить
///////////////////////
void LoadData()
 {
  while (true)
   {
    try
     {
      AllocateMemoryAndReadFile();
      break;
     }
    catch(NoEnoghtMemory)
     {
      try
       {
         WriteSomeDataToHDD();
       }
      catch(IOError)
       {
        throw LoaderError;
       }
     }
   }
 }
///////////////////////
Обработать
///////////////////////
void Process
 {
  if (datalength < 100) throw CanNotProcess;
 }


Рассмотрим продробно. Сперва вызывается LoadData. внутри вызывается AllocateMemoryAndReadFile. Если всё хорошо, тогда break и цикл завершается. Если же произошла ощибка нехватки памяти (NoEnoghtMemory), то управление переходит к WriteSomeDataToHDD. Если записать часть данных не адлось, то мы ловим исключение IOError. Но наверх передаём LoaderError. Соответственно если наверху словили LoaderError, то ещё выше передаётся CanNotLoad. Здесь важно то, что каждый блок try/catch работаеть только с теми исключениями о которых "знает". То есть DoAllWork ничего не знает о внутреннем устройстве LoadData и о том, где и какие ошибки могут возникнуть, но несмотря на это получает понятные ей ошибки. Соответсвенно LoadData не знает как работает AllocateMemoryAndReadFile, но получает ошибку нехватки памяти в понятной форме. ЧТО ВАЖНО! Если внутри AllocateMemoryAndReadFile вызникнет обика вроде FatalError, то на не будет проглочена LoadData и DoAllWork.
То есть благодаря исключениям, мы можем фильтровать ошибки и отлавливать только те, на которые можем адекватно среагировать.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: try-catch
От: Ассистент Россия  
Дата: 02.05.04 05:42
Оценка:
Здравствуйте, Blitz, Вы писали:

B>Все эти try — catch... больше всего пугает throw...


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


Дело в том, что try — catch предназначен для обработки куска кода, где заранее не известно будет ошибка или нет, а проверить наличие ошибки до выполнения кусочка кода часто сложно.
Достаточно удобно try — catch использовать для передачи ошибки в функцию из которой вызывалась.

Например, разборщик текстов, имеющий:

main(void) - головная функция
parser_text(char *file_name) - парсер всего текста
parser_str(char *str) - парсер отдельной строки


Логика вызова такая: main -> parser_text -> parser_str
В parser_str:
char ss[16]; // строка с ошибкой
int pos=0; // текущая позиция в строке
try{
  // Код потенциально пораждающий ошибку
   ...
  pos++;
}
catch(...){
  // Поймали ошибку, передаем информацию об ошибке вызывающей функции
  itoa(ss,pos,16);
  throw ss;
}


В parser_text:
int pos=0;
char error[50]; // текст ошибки
try{
  // Парсер строки
  parser_str(str);
  ...
  pos++;
}
catch(char * ss){
  // Поймали ошибку, передаем информацию об ошибке вызывающей функции
  // добавляем информацию на данном уровне в переменную error
  ...
  throw error;
}



В main:
try{
  // Парсер строки
  parser_text(str);
}
catch(char *ss){
  // Выводим ошибку
  cout << "Ошибка в строке" << ss;
}
Человека с характером трудности привлекают, потому что только при помощи них он может осознать свой потенциал. (c)Шарль де Голль
Re[2]: try-catch
От: Аноним  
Дата: 02.05.04 06:23
Оценка: 18 (1) +1
A>
A>void func()
A> {
A>  throw 0; // Собственно сгенерировали исключение.
A> }
A>////////////
A>void func2()
A> {
A>  try
A>   {
A>    func();
A>   }
A>  catch
A>   {
A>   }
A> }
A>

Это не C# чтобы так писать! В этом коде синтаксическая ошибка после catch!

A>Механизм исключенйи позволяет отсеивать "чужие" ошибки


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

A>
A>catch(...)
A>

Ок...

A>Мы отлавливаем все исключения, но когда пишем так

A>
A>catch(int)
A>///////////////////////////////
A>catch(CMyException, int, float)
A>

A>Мы явно указываем исключения какого типа хотим отлавливать.
Вторая строчка — это еще что такое?

A>
A>////////////////////////
A>Загрузить и обработать данные.
A>////////////////////////
A>void DoAllWork()
A> {
A>  try
A>   {
A>    LoadData();
A>   }
A>  catch(LoaderError)
A>   {
A>    throw CanNotLoad;
A>   }
A>  Process();
A> }
A>///////////////////////
A>Загрузить
A>///////////////////////
A>void LoadData()
A> {
A>  while (true)
A>   {
A>    try
A>     {
A>      AllocateMemoryAndReadFile();
A>      break;
A>     }
A>    catch(NoEnoghtMemory)
A>     {
A>      try
A>       {
A>         WriteSomeDataToHDD();
A>       }
A>      catch(IOError)
A>       {
A>        throw LoaderError;
A>       }
A>     }
A>   }
A> }
A>///////////////////////
A>Обработать
A>///////////////////////
A>void Process
A> {
A>  if (datalength < 100) throw CanNotProcess;
A> }
A>

Не в обиду, но думаю, что все это только больше заставит новичка паниковать.
Re[2]: try-catch
От: Кодт Россия  
Дата: 02.05.04 07:02
Оценка:
Здравствуйте, adontz, Вы писали:

<>

Симпатично, но дофига синтаксических ляпов.

Ещё стоит заметить, что ловятся объекты-исключения заявленного типа,
try
{ ... }
catch(some_exception_type ex)
{ ... }

а кидаются-то экземпляры!
throw some_exception_type(ctor-args);

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

Аналогия из обычного программирования — объявление функции с одним аргументом
void catcher(some_exception_type ex)
и её вызов:
catcher(some_exception_type(ctor-args)).
С той лишь разницей, что мы не наматываем, а разматываем стек — т.е. не заходим внутрь catcher(), а выходим до тех пор, пока не найдём соответствующую инструкцию catch().

Пример содержательной обработки исключения:
struct ComException
{
  HRESULT hr_; // экземпляр несёт сведения о подробностях случившегося
  ComException(HRESULT hr) : hr_(hr) {}
};
// удобная функиция-швырялка (см. ниже)
void TestHresult(HRESULT hr) { if(FAILED(hr) throw ComException(hr); }

.....

// функция возвращает нечто полезное: int, но при этом может кинуть исключение.
int fun()
{
  // там, где "плохой" HRESULT является содержательным и подлежит анализу,
  // или там, где нам пофиг - мы не будем выполнять TestHresult.

  CComPtr<IXXX> pXXX;
  TestHresult( pXXX.CoCreateInstance(CLSID_TheXXX) );

  int v;
  TestHresult( pXXX->GetSomeValue(&v) );

  return v;
}

void main()
{
  try
  {
    int v = fun();
    cout << "fun() == " << v << endl;
  }
  catch(ComException ex)
  {
    cout << "fun() reports an error, HRESULT = 0x" << hex << ex.hr_ << endl;
  }
}
Перекуём баги на фичи!
Re: try-catch
От: Аноним  
Дата: 02.05.04 07:07
Оценка:
Здравствуйте, Blitz,
Если попробовать обяснить все простыми словами, то в try надо засовывать те участки кода, где может произойти какой-то сбой. Как правило, когда приходится обращаться куда-то за данными или обьектами, выделять память и пр. Catch — то самое место , куда программа придет, если такое исключение возникнет. Исключение будет сгенерировано и первично обработано ОС. В Catch мы получим и тип исключения, сообщение и пр. Внутри Catch мы решим как его обработать — т.е. наш аварийный план.
Вполне возможно, что где-то, на достаточно высоком уровне мы создали большой центр по обработке исключений. В таком случае мы можем не возится каждый раз с обработкой , а переправлять исключение наверх, в общий центр. Вот для этого и используем trow. Т.е. trow для нас стрелка, которая переправит ошибку в нужное место. При этом мы можем легко доработать сообщение перед отправкой, добавив к нему свое описание ситуации...
Re[3]: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 02.05.04 07:15
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Это не C# чтобы так писать! В этом коде синтаксическая ошибка после catch!

ДА я собственно не компилировал

А>Вторая строчка — это еще что такое?


Хмм. Мне всегда каазлось, что можно указывать и несколько типов. Пойду поищу откуда я это взял...
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[3]: try-catch
От: sergey_shandar США http://getboost.codeplex.com/
Дата: 02.05.04 07:36
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Ещё стоит заметить, что ловятся объекты-исключения заявленного типа,

К>
К>try
К>{ ... }
К>catch(some_exception_type ex)
К>{ ... }
К>

К>а кидаются-то экземпляры!
К>
К>throw some_exception_type(ctor-args);
К>

К>Это сделано, чтобы можно было передавать в составе исключения разную интересную информацию.

А может лучше так ?
try
{ ... }
catch(some_exception_type &ex)
{ ... }

А то вдруг эту разную интересную информацию потеряем.
getboost.codeplex.com
citylizard.codeplex.com
Re: try-catch
От: Ashen_Angel  
Дата: 02.05.04 08:31
Оценка: -3 :)
Здравствуйте, Blitz, Вы писали:

B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???


Попробуй почитать асмовский листинг, там всё прозрачно, вроде по throw генерируется jmp,
а catch — всего-лишь метка, куда надо прыгнуть. Подробности не помню, давно разбирался.
Re[2]: try-catch
От: achp  
Дата: 02.05.04 11:48
Оценка: +1
Здравствуйте, adontz, Вы писали:

A>
A>int * p = NULL;
A>*p = 3;
A>

A>В данном случае будет попытка разыменовать указатель который не ссылается на переменную типа int. Это ошибка. Такая ошибка вызовет исключение.

Не вызовет. По крайней мере, исключение в смысле Си++ вызвать не должно. В общем случае — вообще не диагностируемо.

MSVC++ "по собственной инициативе" позволяет поймать такое исключение в блоке "catch (...)". Однако, как мы уже выясняли, такое поведение — чистой воды расширение, и конструкция "try-catch" может даже оказаться выброшена в процессе оптимизации, если компилятор увидит, что нет способа, каким внутри блока "try" может быть выброшено исключение в смысле Си++.
Re[3]: try-catch
От: Blitz Великобритания  
Дата: 03.05.04 00:03
Оценка:
Здравствуйте, Аноним, Вы писали:

A>>Мы явно указываем исключения какого типа хотим отлавливать.

А>Вторая строчка — это еще что такое?

A>>
A>>////////////////////////
A>>Загрузить и обработать данные.
A>>////////////////////////
A>>void DoAllWork()
A>> {
A>>  try
A>>   {
A>>    LoadData();
A>>   }
A>>  catch(LoaderError)
A>>   {
A>>    throw CanNotLoad;
A>>   }
A>>  Process();
A>> }
A>>///////////////////////
A>>Загрузить
A>>///////////////////////
A>>void LoadData()
A>> {
A>>  while (true)
A>>   {
A>>    try
A>>     {
A>>      AllocateMemoryAndReadFile();
A>>      break;
A>>     }
A>>    catch(NoEnoghtMemory)
A>>     {
A>>      try
A>>       {
A>>         WriteSomeDataToHDD();
A>>       }
A>>      catch(IOError)
A>>       {
A>>        throw LoaderError;
A>>       }
A>>     }
A>>   }
A>> }
A>>///////////////////////
A>>Обработать
A>>///////////////////////
A>>void Process
A>> {
A>>  if (datalength < 100) throw CanNotProcess;
A>> }
A>>

А>Не в обиду, но думаю, что все это только больше заставит новичка паниковать.

Не, не заставит. Скорее поможет. Примеры — это всегда хорошо.
Re[2]: try-catch
От: Blitz Великобритания  
Дата: 03.05.04 00:08
Оценка:
спасибо за ответы, вроде понятно стало. по крайней мере свой первый try-catch написала вчера, весьма успешно =))))
Re[2]: try-catch
От: Аноним  
Дата: 03.05.04 05:04
Оценка:
A_A>вроде по throw генерируется jmp, а catch — всего-лишь метка, куда надо прыгнуть.
Не может такого быть!
Re[4]: try-catch
От: Аноним  
Дата: 03.05.04 05:09
Оценка:
A>ДА я собственно не компилировал
Так я тоже!

A>Хмм. Мне всегда каазлось, что можно указывать и несколько типов. Пойду поищу откуда я это взял...

Можно, но нужно указывать несколько catch'es подряд или наследовать исключения от какого-то общего класса и затем использовать ссылки или указатели в catch. Например: catch (const some_exception_base_class &e).
Re[3]: try-catch
От: Аноним  
Дата: 03.05.04 05:11
Оценка:
B>спасибо за ответы, вроде понятно стало. по крайней мере свой первый try-catch написала вчера, весьма успешно =))))
Ставь код! Будем баги ловить!
Re[2]: try-catch
От: Willi  
Дата: 03.05.04 06:35
Оценка: +1
Здравствуйте, Ashen_Angel, Вы писали:

A_A>Попробуй почитать асмовский листинг, там всё прозрачно, вроде по throw генерируется jmp,

A_A>а catch — всего-лишь метка, куда надо прыгнуть. Подробности не помню, давно разбирался.

Если бы это было так, то пользы от исключений было с гулькин нос. Потому как хватило бы обычных, setjump / longjump.
Основная фишка в том, что при раскрутке стека вызываются деструкторы локальных объектов.
Это позволяет автоматически освобождать захваченные ресурсы, при условии, что человек использует "умные" указатели и т.п.
\/\/i||i
Re[3]: try-catch
От: Ashen_Angel  
Дата: 03.05.04 15:49
Оценка:
Здравствуйте, Willi, Вы писали:

Всё немного сложнее, чем написал я, но в целом я прав.

W>Если бы это было так, то пользы от исключений было с гулькин нос. Потому как хватило бы обычных, setjump / longjump.

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


Думаешь я не смогу реализовать это через обычные jump'ы?
Просто при использовании throw/catch это сделает компилятор.
Re[2]: try-catch
От: Аноним  
Дата: 03.05.04 15:54
Оценка:
Здравствуйте, Ashen_Angel, Вы писали:

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


B>>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???


A_A>Попробуй почитать асмовский листинг, там всё прозрачно, вроде по throw генерируется jmp,

A_A>а catch — всего-лишь метка, куда надо прыгнуть. Подробности не помню, давно разбирался.

Ничего личного, но такие вот ответы — иллюстрация того,
что знание ассемблера может быть вредным
Re[4]: try-catch
От: Willi  
Дата: 03.05.04 16:01
Оценка:
Здравствуйте, Ashen_Angel, Вы писали:

A_A>Думаешь я не смогу реализовать это через обычные jump'ы?


Вполне допускаю мысль, что сможешь. В конце концов первый копилятор C++ писали явно не на C++

Однако, я не понимаю к чему этот вопрос.
Я не собираюсь меряться с тобой "поинтерами"
\/\/i||i
Re[3]: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 03.05.04 16:07
Оценка:
Здравствуйте, Аноним, Вы писали:

A_A>>Попробуй почитать асмовский листинг, там всё прозрачно, вроде по throw генерируется jmp,

A_A>>а catch — всего-лишь метка, куда надо прыгнуть. Подробности не помню, давно разбирался.

А>Ничего личного, но такие вот ответы — иллюстрация того,

А>что знание ассемблера может быть вредным

Никакого знания здесь нет, throw неявно вызывает функцию _CxxThrowException которая вызывает RaiseException
Где там JMP
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: try-catch
От: Аноним  
Дата: 03.05.04 16:09
Оценка:
Здравствуйте, adontz, Вы писали:

А>>Ничего личного, но такие вот ответы — иллюстрация того,

А>>что знание ассемблера может быть вредным

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

A>Где там JMP

Это я слово "знание" забыл в кавычки заключить
Re: Вот так всегда
От: adontz Грузия http://adontz.wordpress.com/
Дата: 03.05.04 16:22
Оценка: +2
Новичок задаст простой вопрос, а потом набегут умные дяди
Автор:
Дата: 20.03.02
и начнут извращатся
Автор: Alex Fedotov
Дата: 24.03.02
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: try-catch
От: ArtDenis Россия  
Дата: 03.05.04 17:09
Оценка:
Здравствуйте, adontz, Вы писали:

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

A>Где там JMP

А jmp, наверное, внутри этой функции!!!
... << RSDN@Home 1.1.2 stable >>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[3]: try-catch
От: Ashen_Angel  
Дата: 03.05.04 19:22
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Ничего личного, но такие вот ответы — иллюстрация того,

А>что знание ассемблера может быть вредным
Знание не может быть вредным.
Читай соседний пост.
Re[5]: try-catch
От: Ashen_Angel  
Дата: 03.05.04 19:23
Оценка:
Здравствуйте, Willi, Вы писали:

W>Однако, я не понимаю к чему этот вопрос.

W>Я не собираюсь меряться с тобой "поинтерами"
Сорри, неправильно написал. Следует читать:
Думаешь нельзя реализовать это через обычные jump'ы?
А то как наезд выглядит
Re[4]: try-catch
От: Ashen_Angel  
Дата: 03.05.04 19:23
Оценка: +1
Здравствуйте, adontz, Вы писали:

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

A>Где там JMP
Упс, я не о том болтаю Писал как-то трейсер для стека, так
там понадобились __try/__except, щас глянул на try/throw/catch — всё
так как ты сказал. В случае с __try/__except всё примерно так, как
описал я (ну только там не jmp ес-но).
Прошу прощения за флейм
Re: try-catch
От: LaptevVV Россия  
Дата: 04.05.04 06:43
Оценка: 7 (3)
Здравствуйте, Blitz, Вы писали:


B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...

B>Все эти try — catch... больше всего пугает throw...
B>НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch???

B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???


Читай сюда! Это первая часть. Вторая — в соседнем сообщении
Кстати и сообщи, как читается.

Глава 4. Исключения
Не бывает правил без исключений
Народный фольклор

При выполнении любой программы бывают аварийные ситуации, вызванные самыми разнообразными причинами. Если мы делаем программу "для себя", то встроить в нее обработку некоторых ошибок с выдачей сообщений труда не представляет. Но в профессиональном программировании программы чаще пишутся не для себя, а для других, поэтому такой метод обработки ошибок неприемлем.

Принципы обработки исключений

В С++ исключение — это объект. Хотя обычно говорят об исключительной ситуации в программе, такая точка зрения мало что дает, так как с ситуацией сделать ничего нельзя. Поэтому в С++ при возникновении исключительной ситуации программа должна генерировать объект-исключение. Собственно, сама генерация объекта-исключения и создает исключительную ситуацию. Такой подход очень удобен, так как с объектами, в отличие от ситуации, мы можем много чего делать. Например, объявлять как обычную переменную, передать его как параметр любым из возможных способов или возвратить в качестве результата. Можно объявлять массивы исключений или включать объекты исключения в качестве полей в другие классы. В дальнейшем мы иногда будем использовать термин "исключение", понимая под этим объект-исключение.

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

Генерируется исключение оператором throw, который имеет следующий синтаксис

throw выражение_генерации_исключения;


Фраза "выражение_генерации_исключения" на практике означает либо константу, либо переменную некоторого типа. Тип объекта-исключения может быть любым, как встроенным, так и определяемым программистом. Например,
throw 7;
throw "Ошибка: деление на ноль!";
throw Message[i];
throw M_PI;


В первом случае объект-исключение — это целая константа, которая может быть условным номером-кодом ошибки. В общем случае этот код ошибки может вычисляться, например
throw 3*v-i;


Выражение, конечно, может быть не только целочисленным.
Второй вариант — это символьная константа, которая фактически является сообщением об ошибке. Если все сообщения об ошибках записаны в массиве, например,
string Message[326];


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


Пустые классы неожиданно пригодились! Однако объявлять переменную совсем не обязательно, можно обойтись и без этого, например
throw NegativeArgument();


Здесь в качестве "выражения генерации исключения" мы использовали явный вызов конструктора без аргументов, и это наиболее часто используемая форма генерации исключения.
Объект-исключение может быть и динамическим, например,
throw new NegativeArgument();


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

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

Сгенерировать исключение — это только полдела. Исключение надо перехватить и обработать. Проверка возникновения исключения делается с помощью оператора try, с которым неразрывно связаны одна или несколько секций-ловушек catch. Оператор try объявляет в любом месте программы контролируемый блок, который имеет следующий вид
try { /* контролируемый блок */ }


Контролируемый блок, помимо функции контроля, обладает функциями обычного блока: все переменные, объявленные внутри него, являются локальными в этом блоке и не видны вне его.
После try-блока обязательно прописывается один или несколько catch-блоков, которые обычно называют обработчиками исключений. Форма записи секции-ловушки следующая:
catch (спецификация_исключения) { /* блок обработки */}


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


Однако в отличие от параметров функций, никаких преобразований по умолчанию не производится. Это означает, что если в программе написан заголовок обработчика,
catch (double e)            // по значению

то попадают в эту секцию только те исключения, тип выражения исключений которых совпадает с double. Оператор
throw 1

генерирует исключение целого типа, поэтому будет обрабатываться секцией-ловушкой с заголовком, в которой прописан целый тип исключения, например
catch (int e)            // по значению

или
catch (int)                // без передачи информации в секцию-ловушку


Во втором варианте спецификации исключения объект-исключение в блок обработки не передается и никак не используется — исключения перехватываются по типу.
Первые две формы предназначены для обработки конкретного типа исключений. Если же на месте спецификации исключения написано многоточие (как в функциях с переменным числом параметров), то такой обработчик перехватывает все исключения.
Работа конструкции try-catch напоминает работу оператора switch. Секции-ловушки похожи на альтернативы case, а catch-блок с многоточием соответствует альтернативе default оператора-переключателя. Если в try-блоке возникает исключение, то начинается сравнение типа сгенерированного исключения и типов параметров в секциях-ловушках. Выполняется тот catch-блок, тип параметра которого совпал с типом исключения. Если такого типа не найдено, но есть catch с многоточием, то выполняется его блок. Если же такого блока нет, то вызывается стандартная функция завершения terminate().
Таким образом, очень важен порядок записи секций-ловушек после контролируемого блока. Если в качестве первого обработчика после try-блока задан catch-блок с параметром-многоточием, то такой обработчик будет обрабатывать все возникающие исключения. До остальных секций-ловушек дело просто не дойдет. Поэтому усвойте следующее простое правило: всегда задавать catch-блок с параметром-многоточием последним.
Выход из секции-ловушки выполняется одним из следующих способов:
1. выполнились все операторы обработчика — происходит неявный переход на оператор, расположенный после конструкции try-catch;
2. в обработчике выполняется оператор goto; разрешается выполнять оператор на любой оператор вне конструкции try-catch; внутрь контролируемого блока и в другую секцию ловушку переход запрещен;
3. в секции ловушке выполняется оператор throw;
4. в обработчике генерируется другое исключение;
После выполнения операторов catch-блока при отсутствии явных операторов перехода или оператора throw выполняются операторы, расположенные после всей конструкции try-catch. Если во время работы в try-блоке не обнаружено исключительной ситуации, то все catch-блоки пропускаются, и программа продолжает выполнение с первого оператора после всех catch.
Интересно, что можно объявлять контролируемым блок, являющийся телом функции. Например, мы можем контролировать все исключения, возникающие в теле главной функции, написав такую конструкцию:
int main()
try { // контролируемый блок – тело функции
      // …
    }
catch(...)
{ // сообщения об исключениях
}

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


В этом случае у нас отсутствуют операторы после блока try-catch. При такой организации конструктора первые два из перечисленных выше способов выхода из секции-ловушки просто невозможны. Поэтому в данном случае в catch-блоке конструктора обязательно должен присутствовать оператор throw. Однако нужно сказать, что не все системы поддерживают стандарт в полном объеме, поэтому такие возможности следует проверять в конкретной системе.
Блоки try-catch могут быть вложенными, причем как в try-блок, так и в catch-блок:
try {    // блок, который может инициировать исключения
    try {     //вложенный блок
    }
    catch(…){    }
}
catch (исключение) {    // обработка исключения
    try {     //вложенный блок
    }
    catch(…){    }
}


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

Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: try-catch
От: LaptevVV Россия  
Дата: 04.05.04 06:47
Оценка:
Здравствуйте, Blitz, Вы писали:


B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...

B>Все эти try — catch... больше всего пугает throw...
B>НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch???
B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???

Другая часть.

Текст функции с программой-клиентом представлен в листинге 4.3.

//Листинг 4.3 Функция вычисления высоты треугольника с генерацией исключений
double Ha(double a, double b, double c)
{ if ((a>0)&&(b>0)&&(c>0))                    // если параметры правильные 
  { if ((a+b>c)&&(a+c>b)&&(b+c>a))             // если треугольник
    { double p = (a+b+c)/2;                    // вычисление высоты
      return 2*sqrt(p*(p-a)*(p-b)*(p-c))/a;  
    }
    else throw "Не треугольник!";            // генерируем исключение
  }
  else throw "Неправильный параметр!";        // генерация исключения
}
int main()
{ try {                            // контролируемый блок
    cout <<Ha(3,4,5)<< endl;        // нормальное вычисление
    cout <<Ha(1,2,3)<< endl;    // генерирует исключение
    cout <<Ha(1,0,3)<< endl;        // не выполняется!
  } 
  catch(const char *s)            // обработка исключения
  { cout << s << endl; }
  return 0;
}


Обратите внимание на то, что в функции не выполняется возврат "аварийного значения" 0 — в данном случае этого не требуется, так как оператор генерации исключения осуществляет выход из функции. При этом выполняется обычный процесс уничтожения локальных объектов, и вызываются все необходимые деструкторы. Этот процесс уничтожения локальных объектов при выходе по исключению называется "раскруткой" стека. Поэтому вернуться в функцию после генерации исключения невозможно — разве что заново ее вызвать.
Первый оператор вывода срабатывает нормально. Во время выполнения второго возникает исключение, и управление передается в секцию-ловушку. При этом пропускается третий оператор вывода. И мы не можем "вернуться" к его выполнению после выполнения секции-ловушки, даже если пометим его, и напишем оператор goto, — по стандарту оператор goto не может использоваться для перехода внутрь контролируемого блока. Но можно передать управление на начало try-блока и выполнить весь контролируемый блок сначала. Правда, в данном случае — без корректировки параметров во втором вызове — это не изменит поведения программы: третий оператор все равно не будет работать.

Наша функция не имеет спецификации исключений. Наличие спецификации в заголовке, например,
double Ha(double a, double b, double c) throw(const char *)

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

или вовсе запрещающая исключения
double Ha(double a, double b, double c) throw()

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

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

При обработке исключения в нашей программе просто выводится сообщение. Однако чрезвычайно полезно иметь дополнительную информацию об ошибке. Например, бывает необходимо знать конкретные значения параметров функции, которые вызвали сбой программы. Однако параметр в секцию ловушку передается только один — объект-исключение. Поэтому, чтобы передать в блок обработки больше информации, мы должны реализовать собственный тип исключений в виде полноценного класса. Этот класс, естественно, должен быть специализирован под задачу.
В нашем случае, кроме сообщения, полезно передать в блок обработки вместе с объектом-исключением значения параметров, которые получила функция (стороны треугольника) и которые вызвали генерацию исключения. Для этого надо в классе-исключении объявить поля и определить конструктор инициализации. В листинге 4.4 представлен текст этого класса-исключения вместе с функцией и тестовой программой.
//Листинг 4.4 Класс-исключение. Передача информации в обработчик  
struct ErrorTriangle
{ double a, b, c;                    // параметры функции
  string message;                    // сообщение
// конструктор
ErrorTriangle(double x, double y, double z, const string& s)
: a(x), b(y), c(z), 
  message(s) {}            // пустое тело
}
// указана спецификация исключений
double Ha(double a, double b, double c) throw(ErrorTriangle) 
{ if ((a>0)&&(b>0)&&(c>0)) 
  { if ((a+b>c)&&(a+c>b)&&(b+c>a)) 
    { double p = (a+b+c)/2;    
      return 2*sqrt(p*(p-a)*(p-b)*(p-c))/a;  
    }
    else throw ErrorTriangle(a,b,c,"Не треугольник!");       // конструктор
  }
  throw ErrorTriangle(a,b,c,"Неправильные параметры!");        // конструктор
}
int main()
{ try {
    cout <<Ha(3,4,5)<< endl;                    // нормально выполняется
    cout <<Ha(1,2,3)<< endl;                // вызывает исключение
    cout <<Ha(1,0,3)<< endl;                    // не выполняется
  } 
  catch(const ErrorTriangle& e)                // получаем полноценный объект
  { cout << e.message << endl;                // используем поля
    cout << e.a <<' '<< e.b <<' '<< e.c << endl;
  }
}


Класс-исключение определен с помощью структуры для того, чтобы в обработчике не иметь проблем с доступом к полям. Три поля double соответствуют сторонам треугольника, а еще одно поле — для сообщения об ошибке. Теперь мы можем использовать для сообщений тип string, так как в структуре объявлен конструктор инициализации, который и заполняет поля. При передаче в конструктор символьной константы как параметра и выполняется преобразование в string.
В операторе throw в качестве выражения генерации исключения прописан явный вызов конструктора. Секция-ловушка получает объект по константной ссылке и использует информацию, предоставленную конструктором для вывода значений на экран.

Могу еще написать, если требуется.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: try-catch
От: LaptevVV Россия  
Дата: 04.05.04 06:51
Оценка:
Здравствуйте, Blitz, Вы писали:


B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...

B>Все эти try — catch... больше всего пугает throw...
B>Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch..
B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???

Третья часть

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

//Листинг 4.9. Проверка "раскрутки" стека
TArray f2 (const TArray &t)                // самая "внутренняя" функция
{ cout << "function f2" << endl;        // отслеживаем вход
  TArray r(t);                            // конструируем массив
  cout << r << endl;                    // выполняется
  throw TArray::bad_Index();            // генерация исключения
  return r;                                // не выполняется!!!!!
}
TArray f1 (const TArray &t)
{ cout << "function f1" << endl;        // отслеживаем вход
  TArray R[] = { t, t, t };              // создается три массива
  cout << R[0] << endl;                    // выполняется
  cout << R[1] << endl;                    // выполняется
  cout << R[2] << endl;                    // выполняется
  TArray r = f2(t);                        // r не создается – не "успевает"
  return r;                                // не выполняется!!!!!
}
TArray f(const TArray& a)
{ cout << "function f" << endl;            // отслеживаем вход
  TArray t = f1(a);                        // t не создается – не "успевает"
  cout << t << endl;                    // не выполняется!!!!
  return t;                                // не выполняется!!!!
}
int main()
{ double a[5] = {1,2,3,4,5};
  TArray B(a, a+(sizeof(a)/sizeof(double)));
  try { f(B);                            // контролируем 
  }
// секции-ловушки
  catch(TArray::bad_Index) {cout <<"Индекс плохой!"<< endl;}
  catch(TArray::bad_Range) {cout <<"Диапазон плохой!"<< endl;}
  catch(TArray::bad_Size)  {cout <<"Размер плохой!"<< endl;}
// непредвиденное исключение  
  catch(...){cout <<"Exceptions!"<< endl;}
  return 0;
}

При запуске программы мы увидим на экране следующий вывод:

function f
function f1
Конструктор-копирования!
Конструктор-копирования!
Конструктор-копирования!
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
function f2
Конструктор-копирования!
1 2 3 4 5
Destuctor
Destuctor
Destuctor
Destuctor
Индекс плохой!
[/ccode]

Последовательность действий в этом примере следующая:
q    в главной функции объявляется массив a и из него конструируется "умный" массив B;
q    выполняется вход в контролируемый блок и вызов функции f, которой передается параметр — "умный" массив B;
q    в функции первым выполняется оператор вывода и на экране появляется первая строка "function f";
q    во второй строке функции f прописано объявление "умного" массива t, однако до вызова конструктора дело не доходит, так как вызывается функция f1;
q    первый оператор функции f1 выводит на экран строку "function f1";
q    далее в функции f1 создается массив R из трех "умных массивов, и конструктор копирования вызывается трижды; 
q    выполняется вывод на экран трех элементов-массивов;
q    далее прописано объявление "умного" массива r, однако вызова конструктора не происходит, так как вызывается функция f2;
q    функция f2 выводит на экран строку "function f2";
q    создается локальный "умный" массив r, конструктор копирования отрабатывает один раз;
q    содержимое массива r выводится на экран;
q    генерируется исключение bad_Index;

В этот момент у нас создан один локальный массив в функции f2 и массив R из трех массивов в функции f1, которые находятся в стеке. При генерации исключения деструктор вызывается четыре раза, что мы и наблюдаем на экране. После этого происходит переход в секцию-ловушку и выводится строка "Плохой индекс". Затем выполняется выход из секции в тело главной функции, и программа завершает работу.
Теперь распределим обработку исключения по разным функциям. Добавим в функцию f1 контролирующий блок, и идентифицируем вывод в секции-ловушке в главной программе (листинг 4.10). 
[ccode]
//Листинг 4.10. Распределенная обработка исключения
TArray f1 (const TArray &t)
{ cout << "function f1" << endl;
  TArray R[] = { t, t, t };  
  cout << R[0] << endl;
  cout << R[1] << endl;
  cout << R[2] << endl;
  try {                             // контролируем вызов f2
        TArray r = f2(t);            // локальный объект
        return r;                    // норамальное завершение
  }
  catch(TArray::bad_Index) 
  { cout <<"f1. Плохой индекс!"<< endl; 
    throw;                            // повторная генерация исключения
  };
 }
int main()
{ double a[5] = {1,2,3,4,5};
  TArray B(a, a+(sizeof(a)/sizeof(double)));
  try { f(B);                            // контролируем 
  }
// секции-ловушки
  catch(TArray::bad_Index) {cout <<"Main. Индекс плохой!"<< endl;}
  catch(TArray::bad_Range) {cout <<"Диапазон плохой!"<< endl;}
  catch(TArray::bad_Size)  {cout <<"Размер плохой!"<< endl;}
// непредвиденное исключение  
  catch(...){cout <<"Exceptions!"<< endl;}
  return 0;
}
[/ccode]
                        
Остальные функции оставим без изменения. При запуске этой программы на экране появится следующее:
[code]
function f
function f1
Конструктор копирования!
Конструктор копирования!
Конструктор копирования!
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
function f2
Конструктор копирования!
1 2 3 4 5                        // до этого момента поведение повторяется
Destuctor                        // при выходе из f2
f1. Плохой индекс!                // секция-ловушка в функции f1
Destuctor                        // выход из f1
Destuctor
Destuctor
Main. Плохой индекс!             // секция-ловушка в main


Поведение программы повторяется до генерации исключения. Выполнение оператора throw приводит к выходу из функции f2, при этом вызывается деструктор. В вызывающей функции обнаруживается секция-ловушка нужного типа, которая и выполняется. В конце ее срабатывает оператор повторной генерации исключения. Происходит выход из функции f1 — при этом трижды вызываются деструктор для уничтожения элементов массива R. В вызывающей функции — а это уже главная функция main, ищется секция-ловушка нужного типа. Она у нас в программе есть, и мы наблюдаем ее работу в последней строке, выведенной на экран.
В связи с тем, что деструктор вызывается для уничтожения объектов при генерации любого исключения, сам деструктор генерировать исключения не должен. Как указывает Герб Саттер [24], "все деструкторы должны разрабатываться так, как если бы они имели спецификацию исключения throw(), то есть ни одному исключению не должно быть позволено выйти за пределы деструктора". Если же это произойдет, то программа завершается аварийно.

Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: try-catch
От: LaptevVV Россия  
Дата: 04.05.04 06:53
Оценка:
Здравствуйте, Blitz, Вы писали:

B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...


B>Все эти try — catch... больше всего пугает throw...

B>НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch???
B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???

И последнее

//Листинг 4.11. Иерархия стандартных исключений
class exception {//… };
  class logic_error : public exception {//… };
     class domain_error : public logic_error {//… };
     class invalid_argument : public logic_error {//… };
     class length_error : public logic_error {//… };
     class out_of_range : public logic_error {//… };
  class runtime_error : public exception {//… };
     class range_error : public runtime_error {//… };
     class overflow_error : public runtime_error {
     class underflow_error : public runtime_error {//… };
  class bad_cast : public exception {//… };
  class bad_alloc : public exception {//… };
  class bad_tipeid : public exception {//… };
  class bad_exception : public exception {//… };
  class ios_base::failure : public exception {//… };


Эта иерархия служит основой для создания собственных исключений и иерархий исключений. Мы можем определять свои собственные исключения, унаследовав их от класса exception. Для работы со стандартными исключениями в программе надо прописать оператор
#include <stdexcept>
Класс exception определен в стандартной библиотеке следующим образом (листинг 4.12).
//Листинг 4.12. Стандартный класс исключения
class exception {
   public:
     exception () throw();
     exception (const exception&) throw();
     exception& operator= (const exception&) throw();
     virtual ~exception () throw();
     virtual const char* what () const throw();
 };


Что означает слово virtual, мы разберемся во второй части книги, а сейчас обратите внимание на то, что все конструкторы и методы имеют спецификацию, запрещающую генерацию исключений. Это и понятно — если механизм исключений сам будет генерировать исключения, хорошего ждать не приходится — программа закончится аварийно.
Как видим, стандартные исключения включают функцию-метод what(). Использование стандартных исключений продемонстрируем с помощью немного модифицированного примера из справочника интегрированной среды Borland C++ Builder 6 (листинг 4.13).
//Листинг 4.13. Использование метода what()
#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;
void f()                     // функция, генерирующая исключение runtime
{ throw runtime_error("Ошибка runtime!"); }
int main ()
{ string s;
  try {                      // контролируем метод replace класса string
         s.replace(100,1,1,'c'); 
      }
  catch (const exception& e)
  { cout << "Получили исключение: " << e.what() << endl; }
  try {   f();                 // контролируем нашу функцию
      }
  catch (const exception& e)
  { cout << "Получили исключение: " << e.what() << endl; }
  return 0;
}


Эта программа выведет на экран две строки:

Получили исключение: basic_string
Получили исключение: a runtime error

Первая строка — это результат работы первого обработчика при неправильном использовании метода replace строки s, вторая – это уже результат явной генерации исключения типа runtime_error в функции f. Обратите внимание на одну замечательную особенность catch-блоков: в качестве типа формального параметра используется базовый класс, тогда как при выполнении в обработчик попадает объект-исключение совсем другого типа — наследника от базового. Это очень важное свойство наследования мы рассмотрим в следующей главе.

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

LVV>Кстати и сообщи, как читается.


Книжку пишешь?
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 04.05.04 09:20
Оценка:
Здравствуйте, LaptevVV, Вы писали:

ИМХО ИМХО ИМХО итбивка исходников мерзкая.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: try-catch
От: bkat  
Дата: 04.05.04 09:48
Оценка:
Немного критики...

В деталях не разбирался, но в глаза бросается отсутствие советов,
когда стоит выбрасывать исключение и что такое "исключительная ситуация" вообще.
Говорить, что исключение — это объект я бы не стал.
В общем стоит добавить советы, когда же настал момент
(исключительная ситуация) для throw...
Если я это просмотрел, то извиняюсь

Этот момент в общем-то не является специфичным для С++, но очень важен.

А в целом написано интересно
Re[3]: try-catch
От: LaptevVV Россия  
Дата: 04.05.04 11:14
Оценка:
Здравствуйте, bkat, Вы писали:

B>Немного критики...


B>а) В деталях не разбирался, но в глаза бросается отсутствие советов,

B>когда стоит выбрасывать исключение и что такое "исключительная ситуация" вообще.
B>б)Говорить, что исключение — это объект я бы не стал.
B>В общем стоит добавить советы, когда же настал момент
B>(исключительная ситуация) для throw...
B>Если я это просмотрел, то извиняюсь

B>Этот момент в общем-то не является специфичным для С++, но очень важен.


B>А в целом написано интересно


Всем сразу:
1. книжку пишу
2. отбивка исходников — это прям из шаблона издательства Питер — я только наши теги вставил.
3. Спасибо за критику — она мне очень нужна.
а) ну так я еще 3 поста тут же написал — там продолжение. Хотя советов пока действительно нет. Эта глава — ликбез по исключениям, так как они ИМХО плохо описываются в доступных книжках.
б) Именно объект! Его же можно определить в виде класса со всеми вытекающими. Возможно вы имели ввиду, что встроенные — это не совсем объекты, так сказать?
в) советы будут, наверное в последующих главах — при разработкке конкретной довольно большой программы.

Еще раз спасибо!
Прошу и дальше
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: try-catch
От: jazzer Россия Skype: enerjazzer
Дата: 05.05.04 12:37
Оценка: 2 (1)
Здравствуйте, Blitz, Вы писали:

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


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

int status;
status = f1();
if (status != OK)
{
   report_status(status);
   kill them all;
}
status = f2();
if (status != OK)
{
   report_status(status);
   kill them all;
}
status = f3();
if (status != OK)
{
   report_status(status);
   kill them all;
}
status = f4();
if (status != OK)
{
   report_status(status);
   kill them all;
}
status = f5();
if (status != OK)
{
   report_status(status);
   kill them all;
}

или в
int status;
status = f1();
if (status != OK) goto err;
status = f2();
if (status != OK) goto err;
status = f3();
if (status != OK) goto err;
status = f4();
if (status != OK) goto err;
status = f5();
if (status != OK) goto err;
goto all_ok;
err:
{
   report_status(status);
   kill them all;
}
all_ok:

или в
int status;
bool all_ok = false;
status = f1();
if (status == OK)
{
   status = f2();
   if (status == OK)
   {
      status = f3();
      if (status == OK)
      {
         status = f4();
         if (status == OK)
         {
            status = f5();
            if (status == OK)
               all_ok = true;
         }
      }
   }
}
if !(all_ok)
{
   report_status(status);
   kill them all;
}


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

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


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

Смог объяснить? :)
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: try-catch
От: FreshMeat Россия http://www.rsdn.org
Дата: 05.05.04 13:42
Оценка:
http://www.rsdn.ru/Forum/Message.aspx?mid=479157&amp;only=1
Автор: Vladimir_K
Дата: 16.12.03

Особенно хороша эта ссылочка
Хорошо там, где мы есть! :)
Re[4]: try-catch
От: alexkro  
Дата: 06.05.04 05:35
Оценка:
Здравствуйте, adontz, Вы писали:

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

A>Где там JMP

На ia64 imho там простейший jump.
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++ — всего лишь инструмент, и решают не поставленую задачу, а задачу — решения поставленной задачи способом наиболее соответствующем тому который-используют-в-своих-книгах-крутые-мужики.
Если в программе нет шаблонов и исключений — это С с классами. Язык пионэров? Мачто на нем непушут, да? :)
Re[7]: try-catch
От: WolfHound  
Дата: 09.05.04 16:16
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

WH>>Знакомые все лица...

AC>Вроде набрудершафт не пили.
Человек с ником hedgehog на соседнем сайте тоже пытался на исключения наезжать...

AC>Про фанатизм и религию идеальной программы я уже говорил.

Ни какого фанатизма. Один прагматизм.

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

AC>Что без RAII и исключений хороший код писать нельзя или писать его сложнее? Ну, это просто пробел в твоем образовании.
Вопрос о мем образовании рекомендую оставить в покое. Давай конкретно. Как переписать этот код без RAII чтобы он получился не мение читабельным и без лишней ручной работы.
AC>Конкретно приведенный тобой пример, слабо изменится, если убрать из него исключения, заменив на goto.
Могу представить еще одну шедевриальную реализацию эой же функции на 144 строки... Там goto использован...
AC>Просто ты привык использовать исключения, и считаешь, что любой другой способ есть лажа.
Все зависит от ситуации. Но для исключительных ситуаций исключения рулят. Болие того код использующий исключения для оповещения о редких ошибках работает не медленней чем на кодах возврата.
AC>Исключения хороши для передачи ошибки на несколько уровней вверх, и не факт, что такая схема обработки ошибок есть верная всегда.
Привиди пример когда это не оправдано.

AC>Еще раз. Я никогда не говорил, что исключения — зло, я говорил, что они не всегда нужны, как и RAII.

AC>И использовать исключения без RAII практически невозможно, но можно использовать RAII без исключений там, где оно естественно.
AC>Нпример, захват COM обьекта.
ИМХО RAII естественно для любого захвата любого ресурса. Затраты на написание врапера сравними с однократным захватом/освобождением ресурса. Но если ресурс во врапере то у меня уже не болит голова ни о исключениях ни о return'ах ни о чем. К томуже что тоже не маловажно не смешивается логика работы с ресурсом с логикой работы алгоритма в котором этот ресурс использован.

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

Веришь нет я тоже. Но после RAII к goto я возвращаться не намерен.

AC>P.S.

AC>Удивительно, но люди часто забывают, что C++ — всего лишь инструмент, и решают не поставленую задачу, а задачу — решения поставленной задачи способом наиболее соответствующем тому который-используют-в-своих-книгах-крутые-мужики.
Да мне глубоко по барабану что крутые мужики используют в своих книжках. Я исхожу из чисто практических соображений.
... << RSDN@Home 1.1.3 beta 1 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[8]: try-catch
От: Alexey Chen Чили  
Дата: 09.05.04 19:15
Оценка:
Здравствуйте, WolfHound, Вы писали:

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


WH>>>Знакомые все лица...

AC>>Вроде набрудершафт не пили.
WH>Человек с ником hedgehog на соседнем сайте тоже пытался на исключения наезжать...
Странно, что мои постинги рассматриваются как наезд. Или любая точка зрения несовпадающая с точкой зрения _гуру_ есть наезд?

WH>... Но после RAII к goto я возвращаться не намерен.

Интересная точка зрения. Особенно противопоставление двух несвязанных вещей. Вместе пользовать не пробывал?

Меня позабавил чей-то постинг в котором было написанно что 'вся логика в RAII' :).
Но что меня действительно удивляет на различных форумах, так это то, что приводит некто какой-то вырожденный пример и делает вывод что _поэтому_ нужно использовать _X_. Когда я говорю, что чувак не прав и _X_ отсюда никак не следует, появляется _гуру_, который пытается меня убедить, что _X_ надо использовать вообще всегда! Что-то мне подсказывает, что вещей, которые надо использовать всегда, и всегда именно так, как считает _гуру_, в природе не существует.

В данном конкретном случае человек сказал что ему _проще_ написать проверку. Его стали уверять, что это совсем не проще, проще понаписать кучу врапперов для скрытия кучи кода, потому, что тогда можно будет испльзовать исключения и это будет проще :)
Имхо проще это так, как проще тому кто пишет, а не абстрактному гуру :)

Есть такой старый анекдот.

Сидит чувак, играет. К ниму подходит другой чувак и говорит
— Занялся бы ты чем нибудь полезным
— А зачем?
— Ну напишешь программу ........ организуешь контору, будешь получать деньги и спокойно играть.
— А я что делаю?
Re[5]: try-catch
От: jazzer Россия Skype: enerjazzer
Дата: 09.05.04 22:39
Оценка: :)
Здравствуйте, Alexey Chen, Вы писали:

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


J>>ага

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

Ничего плохого, когда ты находишься в ситуации, что без них не обойтись.
Здесь — не этот случай.

AC>Что они противоречат идее (религии) идеальной программы?

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

Ничего, если я не буду отвечать на эти выпады?

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

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

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

Дело не только в утечке ресурсов?
Это во-первых.
А во-вторых — ответь на поставленный вопрос: Как бы выглядела твоя функция?

AC>Смысл бросать инт?

Смысл не читать реплики собеседника до конца?
Цитирую себя: "А ведь исключением может быть не только захудалый int, а любой класс, который может нести в себе кучу полежной информации, а не только статус."

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

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

Так что никаких сферических коней в вакууме.

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


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

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

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


Да, существует. И оборачивается адом для программистов. См. выше.

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

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

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


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

AC>О чем я собственно и сказал, и исключения там, как козе баян.
приведенный выше — это приведенный тобой, с goto. Будь внимательнее

AC>Еще надо умеючи писать врапперы для вызова функций API/библиотек который ничего о RAII не знают.

Все надо делать умеючи.
Посоревнуемся в трюизмах?
AC>Что уже к исключениям имеет самое прямое отношение, так как finally в C++ не предусмотрен.
На тему finally в С++ уже была дискуссия, и я в ней высказался вполне определенно, так что ты споришь не с тем человеком.
AC>Имхо, лишняя работа которая обычно нафиг не нужна.
Если у тебя один единственный вызов данной функции API во всей программе — да, это лишняя работа.

Имхо, дискуссия стремительно теряет смысл
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: try-catch
От: Alexey Chen Чили  
Дата: 09.05.04 23:32
Оценка:
Здравствуйте, jazzer, Вы писали:

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

AC>>В конкретном примере, который я назвал вырожденым, ловится инт, а для остальных ошибок происходит утечка ресурсов.
J>Дело не только в утечке ресурсов?
J>Это во-первых.
J>А во-вторых — ответь на поставленный вопрос: Как бы выглядела твоя функция?

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

AC>>Смысл бросать инт?

J>Смысл не читать реплики собеседника до конца?
J>Цитирую себя: "А ведь исключением может быть не только захудалый int, а любой класс, который может нести в себе кучу полежной информации, а не только статус."

Угу, а использовать статическую (ThreadLocal) унифицировнную структуру для хронения информации?


J>Этот пример не является вырожденным.

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

Ну и аргументы у вас WolfHound'ом. А наша команда легко и непринужденно реализуют надежный и прозрачный код и без исключений и даже, страшно подумать, без RAII. И что из этого следует?
И надо заметить не стонут, поскольку это вообще мелочь на которую внимания то осбого не обращают. Всего лишь задача выбора инструмена в заданных условиях. Выбрали и пошли работать. То, что кто-то владеет только исключениями и без них писать не может.... ну, это не аргумент. А многоэтажные ифы... спать больше надо.


J>Имхо, дискуссия стремительно теряет смысл :(

Ее и не было. Просто на мое замечание, о достоинствах исключений в указанном примере, мне стали втирать, что исключения это круто и без них никуда.
Re[9]: try-catch
От: WolfHound  
Дата: 10.05.04 12:45
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

И опять нет технических аргументов
Перепиши мой код от сюда без RAII
Re[5]: try-catch
Автор: WolfHound
Дата: 09.05.04
... << RSDN@Home 1.1.3 beta 1 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[10]: try-catch
От: Alexey Chen Чили  
Дата: 10.05.04 13:48
Оценка:
Здравствуйте, WolfHound, Вы писали:

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


WH>И опять нет технических аргументов :(

WH>Перепиши мой код от сюда без RAII
WH>Re[5]: try-catch
Автор: WolfHound
Дата: 09.05.04


Зачем? Для мазахизма?
Текнический аргумент? Ты добавил в код COM логику обработки C++ исключений, и если заменить в твоем коде выброс исключения на выход за пределы блока, ничего не изменится. Следовательно применение исключений в данном коде всего лишь твоя привычка.
Их можно там применять, а можно не применять. Но приводить данный пример как аргумент того, что их там применять НАДО....

У вас, что у тебя, что у jazzer'а, что у A_K очень похожие способы спора. Свести все к чему-то не связанному с вопросом (например умным указателям) и сказать, что точка зрения опонента не верна, потому что без умных указателей, этот конкретный код будет плохим. Если что, мы говорили про исключения и про то, что они требуют испльзовать RAII не только для умных указателей, но для того для чего RAII не предназначен, а именно вызова внешних финализаторов ничего о RAII незнающих.


Еще раз повторю. Я не увидел не от тебя не от jazzer'а примера где их (исключения) НАДО использовать... странно да. Хотя написать такой пример весьма несложно.
Re[11]: try-catch
От: Шахтер Интернет  
Дата: 11.05.04 00:41
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

AC>Еще раз повторю. Я не увидел не от тебя не от jazzer'а примера где их (исключения) НАДО использовать... странно да.


AC>Хотя написать такой пример весьма несложно.


Заинтриговали. А можно конкретнее?
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[12]: try-catch
От: Alexey Chen Чили  
Дата: 11.05.04 01:57
Оценка: 5 (1)
Здравствуйте, Шахтер, Вы писали:

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


AC>>Еще раз повторю. Я не увидел не от тебя не от jazzer'а примера где их (исключения) НАДО использовать... странно да.


AC>>Хотя написать такой пример весьма несложно.


Ш>Заинтриговали. А можно конкретнее?


Дык, это...

Ситуация когда класс не может быть создан, например ошибка при создании базового класса или агрегата.
Это специфика C++, и изменение такого способа сообщения о ошибке может привести к изменению архитектуры.
Что вобшем-то не есть плохо, но это уже будет совсем другой код.

Ситуация когда возникла ошибка при передаче верменного обьекта куда либо, как другого обьекта. Или отложенные вчисления.
cerr << ost.str().c_str() << endl;
или
com_object1->oproperty1->DoAnythingWithBSTR("text");
Разруливается и без исключений, но может серьезно ухудшить жизнь.

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

Это то, что первое в голову пришло, но думаю не единственное.

А прервать последовательность исполнения функций можно и без исключенй. Тут уже дело привычки.
Re[13]: try-catch
От: Шахтер Интернет  
Дата: 11.05.04 03:01
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

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


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


AC>>>Еще раз повторю. Я не увидел не от тебя не от jazzer'а примера где их (исключения) НАДО использовать... странно да.


AC>>>Хотя написать такой пример весьма несложно.


Ш>>Заинтриговали. А можно конкретнее?


AC>Дык, это...


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

AC>Это специфика C++, и изменение такого способа сообщения о ошибке может привести к изменению архитектуры.
AC>Что вобшем-то не есть плохо, но это уже будет совсем другой код.

+)

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

AC>cerr << ost.str().c_str() << endl;
AC>или
AC>com_object1->oproperty1->DoAnythingWithBSTR("text");
AC>Разруливается и без исключений, но может серьезно ухудшить жизнь.

+)

AC>Ситуация когда нужен откат через много уровней вложенности. Например парсер без восстановления после ошибки.

AC>Можно и через код возврата, но специфика такая, что это не последовательность действий которую надо прервать, а иерархия из которой надо выйти.

+)

AC>Это то, что первое в голову пришло, но думаю не единственное.


AC>А прервать последовательность исполнения функций можно и без исключенй. Тут уже дело привычки.


Можно сказать -- острой необходимости нет.

Хороший набор примеров. Мне как-то в голову сразу не пришло ничего.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[13]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 11.05.04 03:49
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

AC>А прервать последовательность исполнения функций можно и без исключенй. Тут уже дело привычки.


А вот тебе ещё один пример за исключения, а не код возврата в любом случае...

Пример 1:

int SomeObj::get_val()
{
    int val = 0;
    // делаем полезную работу, при ошибке выкидываем исключения
    return val;
}

//...

try
{
    int val = obj->get_val();
}
catch(...)
{
    std::cerr << "Произошла ошибка" << std::endl;
}


Пример 2:

ErrCode SomeObj::get_val(int *retVal)
{
    ErrCode code = ErrFail;
    if(retVal != 0)
    {
        int val = 0;
        // делаем полезную работу, при ошибке передаём управление на точку выхода
        retVal = val;
        code = ErrOk;
    }
    return code;
}

//...

int val;
ErrCode code = obj->get_val(&val);
if(code == ErrOk)
{
}
else
{
    std::cerr << "Произошла ошибка" << std::endl;
}


Вот даже если посчитать в строчках кода то первый пример сурово короче... Написать врапперы можно один раз и использовать их везде, а если с goto, кодами возвратов и врапперами, то придётся лабать код наподобие примера 2 каждый раз...
Лично я выбираю вариант 1.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[14]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 11.05.04 03:51
Оценка:
Сорри ошибся надо было написать так:
а если с goto, кодами возвратов и без врапперов

MN>...а если с goto, кодами возвратов и врапперами...
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[14]: try-catch
От: Шахтер Интернет  
Дата: 11.05.04 04:07
Оценка:
Здравствуйте, Mr. None, Вы писали:

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


AC>>А прервать последовательность исполнения функций можно и без исключенй. Тут уже дело привычки.


MN>Вот даже если посчитать в строчках кода то первый пример сурово короче...


Что-то у меня не получается сурово короче. Это в строчках. А если в тактах, то это ещё бабушка на двое сказала, что лучше, потому что невинно выглядящий try на самом деле кое-что делает.
Реальная экономия тут будет, если в блоке try catch серия таких вызовов.

MN>Написать врапперы можно один раз и использовать их везде, а если с goto, кодами возвратов и врапперами, то придётся лабать код наподобие примера 2 каждый раз...

MN>Лично я выбираю вариант 1.

Хорошая мысль. Без фанатизма.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[7]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 11.05.04 04:07
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

AC>Если в программе нет шаблонов и исключений — это С с классами. Язык пионэров? Мачто на нем непушут, да?


Совсем нет. Язык C — прекрасный язык в рамках структурной парадигмы программирования. Писать на нём тоже надо уметь: структурный подход — это вам тоже не в тапки писать, пионэрам не осилить (загляните в код ядра W2K и вы поймёте о чём я говорю).
А вот что делают пионэры и делают очень зря — это смешивают парадигмы структурную и объектно-ориентированную (то что вы называете C с классами). При этом возникают не сферические кони в вакууме, а вполне конкретные проблемы, одна из которых, например, — это доказательство правильность ПО. Подходы к решению этой проблемы в структурном и объектно-ориентированном программировании различные. И смешивая в своей программы разные стили вы запутываете этот процесс. А есть ещё проблемы проектирования архитектуры, сбора требований и многое многое другое.
И ещё один комментарий.. Можно вообще писать так:
main()
{
    asm
    {
        ...
    }
}

но ведь никто так не делает...
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[15]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 11.05.04 04:22
Оценка: +1
Здравствуйте, Шахтер, Вы писали:

Ш>Здравствуйте, Mr. None, Вы писали:


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


AC>>>А прервать последовательность исполнения функций можно и без исключенй. Тут уже дело привычки.


MN>>Вот даже если посчитать в строчках кода то первый пример сурово короче...


Ш>Что-то у меня не получается сурово короче. Это в строчках. А если в тактах, то это ещё бабушка на двое сказала, что лучше, потому что невинно выглядящий try на самом деле кое-что делает.

Ш>Реальная экономия тут будет, если в блоке try catch серия таких вызовов.

В строчках:
пример 1 — 14
пример 2 — 21

И это только для одной функции.

Реалии современного ИТ-мира таковы, что работа программиста стоит дороже нового процессора. Поэтому проблему производительности проще решать заменой вычислительной базы, а не оптимизацией, ведущей к усложнению читабельности кода.
Такты считали в конце позапрошлого десятилетия, сейчас считают деньги. Никто не будет платить за доработку программы столько же, сколько за её разработку. А чтобы дорабатывать программу (читай сопровождать) было проще, она должна быть как минимум понятна и легко читаема сопровождающим программистом.
Какой вариант лучше читаем? IMHO тот, где меньше строчек, прозрачнее переходы, понятнее названия и читабельнее сигнатуры.
Чтобы меня не обвинили в отсутствии конкретики, привожу реальный пример.
У меня был опыт, когда клиент (немец) выдал исходники и сказал: писать заново дорого и долго, а проекты похожы, давай-ка отталкиваясь от имеющихся исходников напиши то, что мне надо... Код представлял из себя какую-то мешанину из подходов, стилей и пр. и др., изабиловал непонятными goto и чёрт знает чем ещё... У меня ушло на "доработку" недели 3, причём приложение было очень нестабильным. На следующем проекте я сказал этому клиенту — я буду писать заново, из старого буду использовать только пару библиотек. На разработку с нуля у меня ушло 2 недели, хотя проект был сурово сложнее. Потом модификацией данного кода было создано ещё 2 проекта примерно за неделю каждый — один делал я, другой мой приемник. Жалоб на них не было. Выводы делайте сами.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re: try-catch
От: Vamp Россия  
Дата: 11.05.04 11:01
Оценка:
На самом деле, фокус там не в том, как работает, а в том, когда использовать. Решить, что должна функция делать в результате ошибки — возвращать соответствующий код возврата или бросать исключение не всегда просто, особено если разрабатываешь универсальный код.
Хороший пример — оператор new. Почему он бросает исключение, а не возвращает NULL в случае неуспеха? Мне кажется, по двум причинам:
1) ситуация достатчно редкая, значит вставлять проверку в виде if после каждого вызова нерационально
2) сделать что-либо разумное в этом случае зачастую нельзя — значит, в случае сбоя остается только вывести сообщение и завершить программу. А для этого будет достаточно самого "большого" try в начале программы.
А вот, например, функция открытия файла. Обратный пример. Ошибка открытия файла совершенно не должна приводить к возникновению исключения — мало ли что будет сделано потом, может не открылся — и ладно, новый выберем, а ситуация это распространенная. Дело в том, что синхронизовать выполнение программы после исключения с "нормальным" зачастую бывает непросто.
Да здравствует мыло душистое и веревка пушистая.
Re[8]: try-catch
От: Alexey Chen Чили  
Дата: 11.05.04 12:30
Оценка:
Здравствуйте, Mr. None, Вы писали:

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


AC>>Если в программе нет шаблонов и исключений — это С с классами. Язык пионэров? Мачто на нем непушут, да?


MN>Совсем нет. Язык C — прекрасный язык в рамках структурной парадигмы программирования. Писать на нём тоже надо уметь: структурный подход — это вам тоже не в тапки писать, пионэрам не осилить (загляните в код ядра W2K и вы поймёте о чём я говорю).

MN>А вот что делают пионэры и делают очень зря — это смешивают парадигмы структурную и объектно-ориентированную (то что вы называете C с классами). При этом возникают не сферические кони в вакууме, а вполне конкретные проблемы, одна из которых, например, — это доказательство правильность ПО. Подходы к решению этой проблемы в структурном и объектно-ориентированном программировании различные. И смешивая в своей программы разные стили вы запутываете этот процесс. А есть ещё проблемы проектирования архитектуры, сбора требований и многое многое другое.

Для начала давайте не будем смешивать проектирование и программирование, вещи конечно связанные, но не так как вы написали.
Кстати, где вы на практике видели доказательство правильности работы ПО? Вопрос чисто из интереса.

Во-вторых:

C с классами как золушка индустрии программирования, множество проектов делается именно на этом языке но формально считается что писать на нем зазорно и идеологически неверно.

Имхо, в процессе становления C++ выработался язык более простой чем C++ (народный так сказать), более дешовый при применении в разработке чем C или С++, менее требовательный к железу и компиляторам чем pure С++. Этот язык результат естественной эволюции. Можно конечно его отрицать и говорить что мы, мачо, на такой фигне не пишем. Пользуемся только OO или только структурным программированием. И вообще видим только черное и белое. Только действительность то цветная.

Компиляторы тоже разные бывают. Если данный компилятор не поддерживает исключения и частичную специализацию будем писать на C?
Стоит ли из за таких мелочей от RAII и параметризации отказываться?

Тут кто-то заметил, что стоимость работы программиста стоит дороже процессора. Это смотря какие программы писать. Если у вас конкретная железка например XBox, то вы не в силах повлиять на процессор. Если вы пишете софтину управляющую железом, которое потом пойдет в серию в оооочень большом количестве, то стоимость работы программиста, это сильно обсуждаемая вешь.
Re[9]: try-catch
От: LaptevVV Россия  
Дата: 11.05.04 12:52
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

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

Опишу собственный опыт "смешения". Писал я на Pascal Oregon SoftWare на PDP-11 ЕСКД-докуметратор. Постановка была очень хорошая — все рамочки выверены до милиметра (а печатать, кто помнит, надо было на АЦПУ ). Я поступил очень просто. Исходный текст документа представлял собой текстовый файл, редактируемый редакторм TED через диплей. В текстовый файл вставлялись управляющие операторы вроде "Раздел", "Подраздел", "Содержание" и так далее. Прогриамма читала файл с управляющими операторами и выдавала "выстроенный" по ЕСКД в рамочках документ тоже в виде файла, который оставалось только напечатать. Совершенно типичный подход для того времени и того ПО.
Естественно — основная работа — со строками, которых в том паскале — не было, поэтому я реализовал собственные. На входе-то строки переменной длины, а на выходе — фиксированной! — по ширине рамочки! И черт меня дернул (блин, конечно это был программистский змей-искуситель — эффективность) сделать второй вид строк — для выходного документа. Сейчас я могу только над собой. А тогда мне было не до смеха — я проклял все на свете, отлаживая два механизма строк да еще и преобразовыания между ними. С тех пор я понял: "смешивать" в программировании что бы то ни было — категорически не рекомендуется!

AC>Кстати, где вы на практике видели доказательство правильности работы ПО? Вопрос чисто из интереса.

Мой дипломник доказал правильность разработанных программ собственного диплома. В промышленности, действительно, не видать.
AC>Во-вторых:
AC>C с классами как золушка индустрии программирования, множество проектов делается именно на этом языке но формально считается что писать на нем зазорно и идеологически неверно.
А ерунда! Писать можно на чем угодно, лишь бы работало. И программы сдавались в срок.

AC>Имхо, в процессе становления C++ выработался язык более простой чем C++ (народный так сказать), более дешовый при применении в разработке чем C или С++, менее требовательный к железу и компиляторам чем pure С++. Этот язык результат естественной эволюции. Можно конечно его отрицать и говорить что мы, мачо, на такой фигне не пишем. Пользуемся только OO или только структурным программированием. И вообще видим только черное и белое. Только действительность то цветная.

Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[16]: try-catch
От: Шахтер Интернет  
Дата: 12.05.04 02:23
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>В строчках:

MN>пример 1 — 14
MN>пример 2 — 21

Давай чуть-чуть подредактируем.

ErrCode SomeObj::get_val(int &retVal)
{
    // делаем полезную работу, при ошибке делаем return <код ошибки>
    retVal = ...;
        
    return ErrOk;
}

//...

int val;

if( ErrCode code = obj->get_val(val) )
{
 std::cerr << "Произошла ошибка " << GetErrorDescription(code) << std::endl;
}
else
{
}


Те же 14 строчек.

MN>И это только для одной функции.


MN>Реалии современного ИТ-мира таковы, что работа программиста стоит дороже нового процессора.


У разных людей разные реалии. Тут всё зависит от процессора. Да и программисты бывают китайские.

MN>Поэтому проблему производительности проще решать заменой вычислительной базы, а не оптимизацией, ведущей к усложнению читабельности кода.


Не всегда проще. Более того, если ты вспомнишь, например, мощность процессора, летавшего недавно на Марс, то поймешь, что в некоторых случаях просто невозможно (кстати, сколько этот процессор стоил, с учетом доставки, а?).

MN>Такты считали в конце позапрошлого десятилетия, сейчас считают деньги.


На самом деле, и тогда, и сейчас считают деньги.

MN>Никто не будет платить за доработку программы столько же, сколько за её разработку.


ХЗ. Всякое бывает.

MN>А чтобы дорабатывать программу (читай сопровождать) было проще, она должна быть как минимум понятна и легко читаема сопровождающим программистом.


Золотые слова. К сожалению, реальность очень часто не следует им.

MN>Какой вариант лучше читаем? IMHO тот, где меньше строчек, прозрачнее переходы, понятнее названия и читабельнее сигнатуры.

MN>Чтобы меня не обвинили в отсутствии конкретики, привожу реальный пример.
MN>У меня был опыт, когда клиент (немец) выдал исходники и сказал: писать заново дорого и долго, а проекты похожы, давай-ка отталкиваясь от имеющихся исходников напиши то, что мне надо... Код представлял из себя какую-то мешанину из подходов, стилей и пр. и др., изабиловал непонятными goto и чёрт знает чем ещё... У меня ушло на "доработку" недели 3, причём приложение было очень нестабильным. На следующем проекте я сказал этому клиенту — я буду писать заново, из старого буду использовать только пару библиотек. На разработку с нуля у меня ушло 2 недели, хотя проект был сурово сложнее. Потом модификацией данного кода было создано ещё 2 проекта примерно за неделю каждый — один делал я, другой мой приемник. Жалоб на них не было. Выводы делайте сами.

Всё это здорово, но по-моему, не совсем в тему.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[2]: try-catch
От: Шахтер Интернет  
Дата: 12.05.04 03:03
Оценка:
Здравствуйте, Vamp, Вы писали:

V>На самом деле, фокус там не в том, как работает, а в том, когда использовать. Решить, что должна функция делать в результате ошибки — возвращать соответствующий код возврата или бросать исключение не всегда просто, особено если разрабатываешь универсальный код.

V>Хороший пример — оператор new. Почему он бросает исключение, а не возвращает NULL в случае неуспеха? Мне кажется, по двум причинам:
V>1) ситуация достатчно редкая, значит вставлять проверку в виде if после каждого вызова нерационально
V>2) сделать что-либо разумное в этом случае зачастую нельзя — значит, в случае сбоя остается только вывести сообщение и завершить программу. А для этого будет достаточно самого "большого" try в начале программы.
V>А вот, например, функция открытия файла. Обратный пример. Ошибка открытия файла совершенно не должна приводить к возникновению исключения — мало ли что будет сделано потом, может не открылся — и ладно, новый выберем, а ситуация это распространенная. Дело в том, что синхронизовать выполнение программы после исключения с "нормальным" зачастую бывает непросто.

Исключения хороши для обработки ошибок класса авария. В тех случаях, где остаётся только грести побыстрому до ближайшей удобной бухты. А в остальных случаях -- делеко не факт.
Ошибки ввода пользователя, например. Логично переспросить неверно введённые данные.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[9]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 12.05.04 04:34
Оценка:
Здравствуйте, Alexey Chen, Вы писали:

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

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

AC>Кстати, где вы на практике видели доказательство правильности работы ПО? Вопрос чисто из интереса.

В случае структурного программирования только в университете. Для структурного программирования применяются формально-математические методы (не знаю применяют ли их в промышленности хотя бы ограниченно). Я не считаю себя знатоком структурного подхода, поэтому в реальной жизни этим методом и такими способами доказательства не пользовался.
В случае ООП эту задачу решаю регулярно. В силу определённых причин (в числе которых сама идеология ООП и сложность разрабатываемых в этой идеологии проектов) достаточными условиями доказательства правильности ПО, написанного с использованием объектно-ориентированной парадигмы, являются:
1) соблюдение всем приложением высокоуровневых требований заказчика;
2) соблюдение объектами системы контрактов системного уровня.
Залогом выполнения этих требований является грамотное проектирование и ведение проектной документации (к которой относится и код) в течении всего времени разработки. Техническими способами проверки являются, например, такие механизмы, как:
1) грамотное, однозначное и безопасное оформление интерфейсов, например, расстановка модификаторов const, спецификация списка исключений и т.д. и т.п.
2) проверка пред- и пост- условий обработки сообщений, она не реализована на уровне языка C++, так как например в языке Efiel, но может быть запросто реализована на уровен библиотек;
3) всеобъемлющее тестирование, в случае использования полных юнит-тестов для каждого класса системы возможен отказ от пункта 2.

AC>C с классами как золушка индустрии программирования, множество проектов делается именно на этом языке но формально считается что писать на нем зазорно и идеологически неверно.

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


AC>более дешовый при применении в разработке чем C или С++,

Вот тут согласен — студентам действительно платят меньше, чем гуру с 15-ти летним опытом

AC>менее требовательный к железу и компиляторам чем pure С++.

Насчёт компилятора — согласен, насчёт железа — нет, вспомни хотя бы пример Мейерса из книги "Эффективное использование STL" со сравнением быстродействия сортировок с помощью функции CRT qsort и алгоритма std::sort...

AC>Можно конечно его отрицать и говорить что мы, мачо, на такой фигне не пишем. Пользуемся только OO или только структурным программированием. И вообще видим только черное и белое. Только действительность то цветная.

Мачо себя не считаю, но пишу только на ООП, стараюсь использовать чистый C++ и вижу действительность в цветных радужных тонах — проблем от этого никаких не испытываю.


AC>Компиляторы тоже разные бывают. Если данный компилятор не поддерживает исключения и частичную специализацию будем писать на C?

Нет — поменяем компилятор.

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

ИМХО очень неудачный пример, на одном из поектов мы писали прошивку для микроконтроллеров на чистом C++, проблем никаких не было.
Кстати, даже прошивку для BIOS`а на ассемблере можно писать с ипользованием объектно-ориентированной парадигмы, правда это очень сложно.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[17]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 12.05.04 05:01
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Здравствуйте, Mr. None, Вы писали:


MN>>В строчках:

MN>>пример 1 — 14
MN>>пример 2 — 21

Ш>Давай чуть-чуть подредактируем.


Ш>
Ш>ErrCode SomeObj::get_val(int &retVal)
Ш>{
Ш>    // делаем полезную работу, при ошибке делаем return <код ошибки>
Ш>    retVal = ...;
        
Ш>    return ErrOk;
Ш>}

Ш>//...

Ш>int val;

Ш>if( ErrCode code = obj->get_val(val) )
Ш>{
Ш> std::cerr << "Произошла ошибка " << GetErrorDescription(code) << std::endl;
Ш>}
Ш>else
Ш>{
Ш>}
Ш>


Ш>Те же 14 строчек.

Угу плюс необходимость лезть в документацию, чтобы выяснить, почему это val изменяется, когда я его передаю по значению.... ААА это он в функцию принимается по ссылке, ну зашибись!
Я думаю, что вызову бурю негодования, если скажу, что использование передачи параметров в функцию по ссылке, есть дурной стиль. Вот из этого кода: obj->get_val(val) можно сделать предположение, что значение val измениться только по названию функции, а если название другое, то это очень сильно отравит жизнь сопровождающему программисту. Ссылки нужны не для этого. И чтобы сразу прервать желания дискутировать на эту тему — отвечать на замечания в адрес этого высказывания я не буду. Если вы не согласны со мной, почитайте Страуструпа, Мейерса, Голуба — у всех есть статья, глава, совет, посвящённый этому вопросу.

MN>>Реалии современного ИТ-мира таковы, что работа программиста стоит дороже нового процессора.

Ш>У разных людей разные реалии. Тут всё зависит от процессора. Да и программисты бывают китайские.
Средняя стимость приложения у оффшорников (а цены здась самые низкие) примерно около 1500$ — 2000$, рейд программера около 5 баксов в час. Цена сопоставима с ценой нового компьютера, но новый компьюетр устарее через 3 месяца, а через полгода подвергнется обновлению, а данные приложения используются заказчиками годами.
Про большие проекты в серьёздных фирмах я вообще молчу... Даже для наших заказчиков стартовая цена лежит в районе 10000$ — имеется в виду, что если заказчик предложит меньше, то с ним точно разговаривать даже не будут. Вот таковы реалии.

MN>>Поэтому проблему производительности проще решать заменой вычислительной базы, а не оптимизацией, ведущей к усложнению читабельности кода.

Ш>Не всегда проще. Более того, если ты вспомнишь, например, мощность процессора, летавшего недавно на Марс, то поймешь, что в некоторых случаях просто невозможно (кстати, сколько этот процессор стоил, с учетом доставки, а?).
И как часто вы писали ПО, летавшее на Марс?

MN>>Никто не будет платить за доработку программы столько же, сколько за её разработку.

Ш>ХЗ. Всякое бывает.
У нас — да. У них — нет.

MN>>А чтобы дорабатывать программу (читай сопровождать) было проще, она должна быть как минимум понятна и легко читаема сопровождающим программистом.


Ш>Золотые слова. К сожалению, реальность очень часто не следует им.


Это некоторые программисты не следуют им.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[2]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 12.05.04 05:08
Оценка:
Здравствуйте, Vamp, Вы писали:

V>Хороший пример — оператор new. Почему он бросает исключение, а не возвращает NULL в случае неуспеха? Мне кажется, по двум причинам:

V>1) ситуация достатчно редкая, значит вставлять проверку в виде if после каждого вызова нерационально
V>2) сделать что-либо разумное в этом случае зачастую нельзя — значит, в случае сбоя остается только вывести сообщение и завершить программу. А для этого будет достаточно самого "большого" try в начале программы.

new бросает исключение по следующей причине:
раньше new возвращал 0, но члены комитета по стандартизации языка C++ перед принятием стандарта 1998 года, выполнили анализ (не знаю чего) и пришли к выводу, что очень большое количество ошибок было связано с тем, что программисты не проверяли значение возвращённое оператором new, поэтому было решено принудить программистов обрабатывать эту ситуацию выбрасыванием исключения.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[3]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 12.05.04 05:16
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Исключения хороши для обработки ошибок класса авария. В тех случаях, где остаётся только грести побыстрому до ближайшей удобной бухты. А в остальных случаях -- делеко не факт.


Исключения — это не обязательно ошибки и тем более ошибки класса авария. Это исключительная ситуация и произойти она может по разным причинам. Как-то раз у меня была ситуация, когда в алгоритме поиска наиболее естественным и удобным результатом в случае не обнаружения искомого элемента был выброс исключения not_founded...

Ш>Ошибки ввода пользователя, например. Логично переспросить неверно введённые данные.


А можно, как вариант, выбросить исключение, которое при срабатывании выведет сообщение с предупреждением и будет отловлено кем-то выше, например методом, который открывает окошко ввода.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[4]: try-catch
От: Павел Кузнецов  
Дата: 12.05.04 05:24
Оценка:
> Ш>Ошибки ввода пользователя, например. Логично переспросить неверно введённые данные.
>
> А можно, как вариант, выбросить исключение, которое при срабатывании выведет сообщение с предупреждением и будет отловлено кем-то выше, например методом, который открывает окошко ввода.

Можно. Только, когда ты захочешь по ходу ввода выдавать тул-тип о том, что данные введены неправильно, может статься, что придется все перепроектировать, избавившись от исключений.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[5]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 12.05.04 05:35
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

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


Ну почему? Одно другому не мешает, но это к теме уже не относится.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[18]: try-catch
От: Шахтер Интернет  
Дата: 13.05.04 00:37
Оценка:
Здравствуйте, Mr. None, Вы писали:

Ш>>Те же 14 строчек.

MN>Угу плюс необходимость лезть в документацию, чтобы выяснить, почему это val изменяется, когда я его передаю по значению.... ААА это он в функцию принимается по ссылке, ну зашибись!
MN>Я думаю, что вызову бурю негодования, если скажу, что использование передачи параметров в функцию по ссылке, есть дурной стиль. Вот из этого кода: obj->get_val(val) можно сделать предположение, что значение val измениться только по названию функции, а если название другое, то это очень сильно отравит жизнь сопровождающему программисту. Ссылки нужны не для этого. И чтобы сразу прервать желания дискутировать на эту тему — отвечать на замечания в адрес этого высказывания я не буду.

MN>Если вы не согласны со мной, почитайте Страуструпа, Мейерса, Голуба — у всех есть статья, глава, совет, посвящённый этому вопросу.


А почему вы решили, что я их не читал?

MN>>>Реалии современного ИТ-мира таковы, что работа программиста стоит дороже нового процессора.

Ш>>У разных людей разные реалии. Тут всё зависит от процессора. Да и программисты бывают китайские.
MN>Средняя стимость приложения у оффшорников (а цены здась самые низкие) примерно около 1500$ — 2000$, рейд программера около 5 баксов в час. Цена сопоставима с ценой нового компьютера, но новый компьюетр устарее через 3 месяца, а через полгода подвергнется обновлению, а данные приложения используются заказчиками годами.
MN>Про большие проекты в серьёздных фирмах я вообще молчу...

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

MN>Даже для наших заказчиков стартовая цена лежит в районе 10000$ — имеется в виду, что если заказчик предложит меньше, то с ним точно разговаривать даже не будут. Вот таковы реалии.


Программы пишутся не только для PC.

MN>>>Поэтому проблему производительности проще решать заменой вычислительной базы, а не оптимизацией, ведущей к усложнению читабельности кода.

Ш>>Не всегда проще. Более того, если ты вспомнишь, например, мощность процессора, летавшего недавно на Марс, то поймешь, что в некоторых случаях просто невозможно (кстати, сколько этот процессор стоил, с учетом доставки, а?).
MN>И как часто вы писали ПО, летавшее на Марс?

Это конфиденциальная информация.

MN>>>Никто не будет платить за доработку программы столько же, сколько за её разработку.

Ш>>ХЗ. Всякое бывает.
MN>У нас — да. У них — нет.

Не будьте столь категоричны.

MN>>>А чтобы дорабатывать программу (читай сопровождать) было проще, она должна быть как минимум понятна и легко читаема сопровождающим программистом.


Ш>>Золотые слова. К сожалению, реальность очень часто не следует им.


MN>Это некоторые программисты не следуют им.


Некоторые???
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[19]: try-catch
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 13.05.04 03:43
Оценка:
Здравствуйте, Шахтер, Вы писали:

MN>>>>Реалии современного ИТ-мира таковы, что работа программиста стоит дороже нового процессора.

Ш>>>У разных людей разные реалии. Тут всё зависит от процессора. Да и программисты бывают китайские.
MN>>Средняя стимость приложения у оффшорников (а цены здась самые низкие) примерно около 1500$ — 2000$, рейд программера около 5 баксов в час. Цена сопоставима с ценой нового компьютера, но новый компьюетр устарее через 3 месяца, а через полгода подвергнется обновлению, а данные приложения используются заказчиками годами.
MN>>Про большие проекты в серьёздных фирмах я вообще молчу...

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

Ш>Программы пишутся не только для PC.

Вы можете не поверить, но я представляю себе этот зоопарк вычислительных платформ, который существует в мире (от кластерных систем до PDA). И вы можете не поверить, но я писал не только для PC под M$ Windows. Но при этом я делаю поправку, что несмотря на это лидером в ИТ-бизнесе является как раз связка PC + M$ Windows. И большинство задач за которые платят реальные деньги решаются на этой платформе (иначе бы M$ не стала монополистом, а мальчик Билли не стал бы самым толстым миллиардером). При этом я не вижу как всё это связано с моим предыдущим высказыванием, я привёл минимальные цены на заказное ПО в России для самой распространённой платформы, где конкуренция просто дикая. Для специфичных платформ цены будут на порядок выше (поверьте мне — я писал для экспериментальной платформы с экспериментальной ОС-кой, заказчик платил даже за моё обучение, а не только за заказ).

MN>>>>Никто не будет платить за доработку программы столько же, сколько за её разработку.

Ш>>>ХЗ. Всякое бывает.
MN>>У нас — да. У них — нет.

Ш>Не будьте столь категоричны.


Хорошо, поскольку я работал с иностранными заказчиками, находясь при этом исключительно в Новосибирске, и могу полностью не ориентироваться в этом вопросе. Давайте спросим у наших коллег, работающих за границей. Что они думают по этому вопросу?
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[20]: try-catch
От: Шахтер Интернет  
Дата: 13.05.04 04:57
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Вы можете не поверить, но я представляю себе этот зоопарк вычислительных платформ, который существует в мире (от кластерных систем до PDA). И вы можете не поверить, но я писал не только для PC под M$ Windows.


Замечательно.

MN>Но при этом я делаю поправку, что несмотря на это лидером в ИТ-бизнесе является как раз связка PC + M$ Windows.


Да нельзя смешивать всё в одну кучу. Есть масса направлений, где PC + M$ Windows не ночевали и ночевать не будут. Есть оборудование, которое ставится один раз и работает до тех пор, пока изоляция не сгниёт на проводах. Никакой железный апгрейд невозможен -- только флеш перепрошивается.

MN>И большинство задач за которые платят реальные деньги решаются на этой платформе (иначе бы M$ не стала монополистом, а мальчик Билли не стал бы самым толстым миллиардером). При этом я не вижу как всё это связано с моим предыдущим высказыванием, я привёл минимальные цены на заказное ПО в России для самой распространённой платформы, где конкуренция просто дикая. Для специфичных платформ цены будут на порядок выше (поверьте мне — я писал для экспериментальной платформы с экспериментальной ОС-кой, заказчик платил даже за моё обучение, а не только за заказ).


Я, честно говоря, уже потерял нить в рассуждениях.

MN>Хорошо, поскольку я работал с иностранными заказчиками, находясь при этом исключительно в Новосибирске, и могу полностью не ориентироваться в этом вопросе. Давайте спросим у наших коллег, работающих за границей. Что они думают по этому вопросу?


Вы можете у меня спросить. Отвечу: бывает всякое. Вообще, представление о том что за границей умеют считать деньги несколько преувеличено.
... << RSDN@Home 1.1.0 stable >>

Удалено избыточное цитирование. -- ПК
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.