powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / C++ exceptions. Best practices.
25 сообщений из 82, страница 1 из 4
C++ exceptions. Best practices.
    #38865800
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Коллеги!

Давайте обсудим C++ exceptions.

1) Best practices.
2) Антипаттерны.

В рамках продолжения Идеология С++ и warnings
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865831
Basil A. Sidorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Исключения, как уже отмечали, появились не в плюсах и появились они для того, чтобы отделить "рабочий поток исполнения" и "обработку ошибок".
Можно имитировать через setjump/longjump, но возникают проблемы с деструкторами объектов: всё, что компилятор трудолюбиво заготовил на стеке - идёт лесом.
Можно имитировать "циклом с прерыванием" - будет минимальное "замусоривание" кода тривиальными вставками вида:
Код: sql
1.
if (error) break;

Насколько я понимаю, в обоих вариантах будут проблемы с многопоточным кодом.
"Как-то так".
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865848
Basil A. Sidorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865858
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добавлю еще пункт.

3) Неперехваченный exception в конструкторе. Будет-ли создан объект?
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865863
Basil A. Sidorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
"Выполнение кода остановится в точке генерации исключения. Деструкторы не вызываются, если конструктор не отработал до конца" - не дословная цитата Скотта Мейерса.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865927
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
За что я не люблю исключения: 99.(9)% людей не умеют их использовать.

В большинстве учебников исключения объяснены на примерах типа такого:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
try {
   File f;
   f.open(somepath);
   string contents = f.readAll();
   f.close();
} catch (Exception e) {
   message("не смогли прочитать файл");
   if(f.isOpen()) f.close();
}


Все мило и замечательно, четко, понятно и вообще красиво.
Но после этого, в реальном коде люди постоянно пишут текст типа:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
try {
   File f;
   f.open(somepath);
   string contents = f.readAll();
   f.rewind();
   ///  делаем что-то с contents
   f.writeFile(contents);
   f.close();
} catch (Exception e) {
   message("не смогли обновить файл");
   if(f.isOpen()) f.close();
}

И потом начинают кручиниться: "А как узнать почему именно упали? Мы не смогли найти файл? Не смогли прочитать его? Не смогли записать? Или произошло что-то еще не связанное с файлом?"
Вот вам конкретный пример такого человеческого поведения: 17179694 . Дмитрий застрял именно на том, что у него есть несколько однотипных операций, все они способны выкинуть одно и то-же исключение. Но ему надо понять какая конкретно операция упала, а он не может этого сделать. Почему не может, хотя метод решения лежит на поверхности? Потому что он привык что в try{}catch блоке находится несколько операторов. И обработка ошибок ВСЕГДА идет по окончанию блока. Ну вот привык человек и все.
Как дополнительный пример этой же самой проблемы, можно ткнуть пальцем в статью Завалишина опубликованную аж в 97-ом, и до сих пор еще читаемую: 17185154 . В статье много эмоций, но... Берем самое начало статьи "Как это делают в С++". Завалишин предлагает два примера (номера 4 и 5) в качестве иллюстрации как удобно с исключениями и как не удобно без них. И на первый взгляд он прав - код с исключениями короче... но.... если чуть-чуть приглядеться, то видно что примеры разные и решают разные задачи по разным алгоритмам. Причем работа надо ошибками в пятом примере более качественная чем в четвертом примере.


Вторая гигантская проблема исключений это то что их можно перекидывать на уровень выше:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
foo1() { ... }
foo2() {
   foo1();
}
foo3() {
   foo1();
   foo2();
}
foo4() {
  try {
     foo3();
  } catch(ExceptionFromFoo1 e) {
     message("Died in foo1()");
  }
}

А теперь попробуйте сказать, какой из вызовов foo1() упал. Тот который был вызван напрямую из foo3() или тот который вызывался через посредничество foo2()?
Примеров подобных вопросов на форуме я сейчас с ходу не нашел, но все кто занимается поддержкой существующего софта встречались с этой проблемой.


Вы можете спросить: ну так а что мешает разрезать пример с обновлением файла на три try{}catch блока? И что мешает написать foo2() во втором примере так чтобы он сам ловил исключение в foo1() и кидал в свою очередь более информативное исключение? Ответ: ничего не мешает кроме привычек.
Человек привыкший к работе с кодами возврата всегда думает в каком логическом блоке он находится. И с самого начала обработает ошибку по месту ее возникновения. При работе с кодами возврата мы вынуждены думать где мы сейчас находимся и что надо делать в случае потенциальной проблемы.
А у человека привыкшего к исключениям такого навыка просто нет. В учебнике сказано что обработку ошибок можно делать в самом конце? Значит мы всегда будем ее делать в самом конце и не важно правильно ли это.



Да, используя исключения можно написать хорошую и качественную программу. Теоретически это возможно. Но к сожалению, в реальности это практически не встречается.
Для правильной работы с исключениями надо понимать что они работают не так как думает человек. Не просто знать, а понимать, чувствовать, принять как дыхание. Мы, люди, когда пишем в программе:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
baz() {
  try {
   call1();
   call2();
   call3();
  }catch(Exception e) {...}
}

Мысленно проговариваем про себя: "вызвали call1(), если упали идем в блок catch, потом вызвали call2(), если упали идем в блок catch, и наконец вызвали call3(), если упали идем в блок catch." И мы думаем что это все именно так и происходит.
Но к сожалению, это вовсе не так. На самом деле этот код означает "Если мы попали в catch, значит какой-то кусок, какой-то из функций не отработал. Причем мы не знаем конкретно какая из функций call1, call2 или call3 не доработала до конца и в какой степени эта функция не отработала до конца".
Если вы сумеете перестроить свое сознание на правильное понимание что такое исключение - вы сможете писать хорошие программы с ними. Практика показывает что таких людей на свете единицы.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865946
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
White Owl, в данном примере хотелось-бы чтобы компиллятор сделал

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
baz() {
  try {
   line:=__LINE__;
   call1();   
   line:=__LINE__;
   call2();
   line:=__LINE__;
   call3();
  }catch(Exception e) {
     message("Died after "+line);
  }
}


Или что-то в этом роде.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865961
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
White OwlЗа что я не люблю исключения: 99.(9)% людей не умеют их использовать.
Ггггггг
Гигантская проблема исключений то что эти 99.9% считают что уж они то умеют. И потом по форумам рассказывают небылицы.
Например про то, что то, ради чего исключения и были придуманы, и без чего они вообще никому не нужны, оказывается - это гиганская проблема:
White OwlВторая гигантская проблема исключений это то что их можно перекидывать на уровень выше
Еще раз повторю то что писал уже в том топике другими словами.
Чем дальше уровень вложенности где ловятся исключения от уровня где они бросаются, тем правильнее исключения используются.
И верхним уровням никогда не требуется знать где конкретно произошло исключение нижнего уровня.
Если вам требуется - то у вас дизайн программы сделан через одно место.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865968
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
baz() {
  try {
   line:=__LINE__;
   call1();   
   line:=__LINE__;
   call2();
   line:=__LINE__;
   call3();
  }catch(Exception e) {
     message("Died after "+line);
  }
}


Вот это пример антипаттерна. Когда ловля исключений используется только для отладки.
Для этого вообще то логирование существует.
Данный код должен выглядеть так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
baz() {
   logger.trace("call1");
   call1();   
   logger.trace("call2");
   call2();
   logger.trace("call3");
   call3();
}


Никаких try catch тут не надо вообще.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865972
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В исключениях есть минусы:
1. нижний уроверь имеет больше конкретики о причине исключения.
2. освобождение ресурсов ОС. Хотя если обернуть все в классы, то решаемо.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38865979
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T2. освобождение ресурсов ОС. Хотя если обернуть все в классы, то решаемо.
Если используются исключения всегда должно применяться RAII.
Иначе смысла в них нет.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866009
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly MoskovskyВот это пример антипаттерна. Когда ловля исключений используется только для отладки.
Для этого вообще то логирование существует.
Данный код должен выглядеть так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
baz() {
   logger.trace("call1");
   call1();   
   logger.trace("call2");
   call2();
   logger.trace("call3");
   call3();
}


Никаких try catch тут не надо вообще.
Ну вроде как по пункту (2) отметились. Повестка двигается.

А теперь вопрос. Что это за объект такой logger? И зачем он мне нужен?
Я не хочу никаких логгеров. Мне выводить - некуда. Нет у меня файловой системы.
Вот такие вот условия.

Может быть такое быть?
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866029
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 Белый Сова.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
try {
   File f;
   f.open(somepath);
   string contents = f.readAll();
   f.rewind();
   ///  делаем что-то с contents
   f.writeFile(contents);
   f.close();
} catch (Exception e) {
   message("не смогли обновить файл");
   if(f.isOpen()) f.close();
}



Ну... если-б каждый метод подсистемы IO бросал свой уникальный expeption.
Код: plaintext
1.
2.
3.
4.
5.
class openexception : exception{...};
class readallexception : exception{...};
class rewindexception : ....
class writeexception :
class closeexception :



То мы могли-бы ловить в коде некий общий тип исключений и позиционироваться более конкретно. Но
опять-же... если в блоке try мы работаем с двумя файлами.... :(
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866041
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonWhite Owl, в данном примере хотелось-бы чтобы компиллятор сделал

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
baz() {
  try {
   line:=__LINE__;
   call1();   
   line:=__LINE__;
   call2();
   line:=__LINE__;
   call3();
  }catch(Exception e) {
     message("Died after "+line);
  }
}


Или что-то в этом роде.Нет. Этого не достаточно.
Падение произошло внутри одной из функций. Мало знать какая функция упала, надо еще знать почему она упала.
Это более-менее хорошо сделано в java, где исключение автоматоматически собирает полный стек вызовов при передаче на верхние уровни. Там уже действительно можно пройти по списку и действительно найти истинный источник ошибки. В С++ это можно сделать и вручную конечно, но это надо делать.
В жизни с кодами возврата конечно все намного труднее потому что там вообще все надо делать вручную. Но в этом и плюс - ты всегда знаешь что если надо ошибку передать наверх, ты будешь ее передавать наверх. Сам. Вручную. Перед этим обдумав. А не просто махнув рукой: "Здесь мы работаем с фигулькой которая может глюкнуть. Но мне лениво сейчас разбираться как это обработать. Выкину исключение, пусть мой коллега который будет работать с этой функцией разбирается". Тот перекидывает исключение выше, а в итоге оно если и обрабатывается, то уже никому не известно откуда оно родилось.

Повторюсь еще раз. С исключениями можно работать, но надо думать что делаешь. Учебники (и форумные гуру) представляют исключение как очень простую и удобную штуку. Как результат: люди используя исключения не пытаются видеть полную картину своей программы.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866042
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly MoskovskyWhite OwlЗа что я не люблю исключения: 99.(9)% людей не умеют их использовать.
Ггггггг Анатолий, прочитайте мой пост еще раз. И на этот раз не по диагонали, и не ограничиваясь примерами.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866050
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton2 Белый Сова.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
try {
   File f;
   f.open(somepath);
   string contents = f.readAll();
   f.rewind();
   ///  делаем что-то с contents
   f.writeFile(contents);
   f.close();
} catch (Exception e) {
   message("не смогли обновить файл");
   if(f.isOpen()) f.close();
}



Ну... если-б каждый метод подсистемы IO бросал свой уникальный expeption.
Код: plaintext
1.
2.
3.
4.
5.
class openexception : exception{...};
class readallexception : exception{...};
class rewindexception : ....
class writeexception :
class closeexception :



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

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
try {
  File f;
  try {
    f.open(somepath);
    string contents = f.readAll();
  } catch (Exception e) {
    message("не смогли прочитать файл");
    throw new ExceptionFailedToUpdate();
  }

  try {
     ///  делаем что-то с contents
  } catch (Exception e) {
    message("не смогли обработать данные");
    throw new ExceptionFailedToUpdate();
  }

  try {
    f.rewind();
    f.writeFile(contents);
    f.close();
  } catch (Exception e) {
    message("не смогли записать файл");
    throw new ExceptionFailedToUpdate();
  }
} catch (ExceptionFailedToUpdate e) {
   message("не смогли обновить файл");
   if(f.isOpen()) f.close();
}


Но... Вот наглядный пример, ты сам. С++ знаешь, с исключениями работать умеешь, но пошел по пути создания множества дополнительных исключений. Почему? Что тебе помешало разбить алгоритм на блоки и сделать для каждого свой обработчик?
Я со своей привычкой к кодам возврата обошелся одним дополнительным, ты сделал пять дополнительных и сам испугался. Почему?
Потому что привык что все исключения обрабатываются в конце. Так в учебниках пишут...
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866057
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тогда делаем макрос

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
#ifdef DETAIL_IO_EX
#define READALL(f,somepath,contents) \
try { \
    f.open(somepath);\
    string contents = f.readAll();\
  } catch (Exception e) {\
    message("не смогли прочитать файл");\
    throw new ExceptionFailedToUpdate();\
  }
#elsif
#define READALL(f,somepath,contents) \
f.open(somepath);\
string contents = f.readAll();
#endif



Хотя возможно до меня этот макрос (класс или шаблон или функцию) уже где-то делали.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866101
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonТогда делаем макросНафиг-нафиг. Наша цель же упростить программирование и не потерять гибкости. Макрос, особенно такой, это усложнение. Так что нафиг-нафиг.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866174
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonА теперь вопрос. Что это за объект такой logger? И зачем он мне нужен?
Я не хочу никаких логгеров. Мне выводить - некуда. Нет у меня файловой системы.
Вот такие вот условия.
Ну тогда вам сначала надо применить это условие по отношению к вашей функции message()
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866182
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
White OwlАнатолий, прочитайте мой пост еще раз. И на этот раз не по диагонали, и не ограничиваясь примерами.
Ну вот читаю еще раз.
White OwlНо ему надо понять какая конкретно операция упала, а он не может этого сделать.
Это нужно программисту расследующему баги. И это решается логированием, как я выше привел.
А вот зачем самой функции в процессе выполнения это знать?
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866258
egorych
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
White OwlЗа что я не люблю исключения: 99.(9)% людей не умеют их использовать.
...
Практика показывает что таких людей на свете единицы.прекрасный спич, но практика показывает, что 99.(9)% вообще не обрабатывают ошибки времени выполнения. Коды возврата технично игнорируются и программа падает в непредсказуемом месте из-за деления на 0 или по защите памяти, к примеру. В большой степени это происходит из-за того, что довольно нудно обрабатывать после каждого вызова этот самый код ошибки. catch в конце блока хотя бы гарантирует, что сломалось где-то здесь, и дальше этого места ошибка не распространится.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866259
egorych
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly MoskovskyWhite OwlНо ему надо понять какая конкретно операция упала, а он не может этого сделать.
Это нужно программисту расследующему баги. И это решается логированием, как я выше привел.
А вот зачем самой функции в процессе выполнения это знать?+1. Золотые слова ))
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866306
locked
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Исключения в C++ состоитят из двух частей

1.механизм
2.имплементация в стандартной библиотеке.

Первое сделано хорошо, второе нет.
Можно добавить логирование backtrace() и __LINE__ при генерировании своих исключений.
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866312
Фотография OoCc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
egorychAnatoly Moskovskyпропущено...

Это нужно программисту расследующему баги. И это решается логированием, как я выше привел.
А вот зачем самой функции в процессе выполнения это знать?+1. Золотые слова ))

выдать одну ошибку кастомеру "Ваша программа неработает" ?
...
Рейтинг: 0 / 0
C++ exceptions. Best practices.
    #38866320
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
OoCcegorychпропущено...
+1. Золотые слова ))

выдать одну ошибку кастомеру "Ваша программа неработает" ?
Уже говорили выше. Я-бы добавил что стоит различать просто факт
возникновения ошибки (например SocketException). После этого, метод
выболняющий к примеру softwareupdate молча уходит в ожидание. Мало-ли
что там с сетью. WiFi отвалился.

И второй вариант - это как сказано выше для анализа ошибок разработчиком.
В этом варианте я не просто хотел-бы получить __LINE__ где "стрельнуло"
но и "посмертный снимок" стека.
...
Рейтинг: 0 / 0
25 сообщений из 82, страница 1 из 4
Форумы / C++ [игнор отключен] [закрыт для гостей] / C++ exceptions. Best practices.
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]