На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (4) [1] 2 3 ... Последняя » все  ( Перейти к последнему сообщению )  
> "обработка исключений" vs "обработка кодов возврата"
    Всем приветище в этом чятике! :lol:

    Так получилось, что большую, скажем так, профессиональную часть своей работы с С++ я провел исключительно в симбиозе с Qt. А там, увы, свои правила. В документации (щя не скажу, или сами найдёте, ну или я потом) написано английским по белому "не используйте обработку исключений, наша либа это категорически не приветствует". Естественно не дословно. И эта часть языка С++ (обработка исключений) до сих пор для меня является чисто академической. Да, я знаю синтаксис, напишу любые примеры применения ... Но это для меня до сих пор является предметом не моей практики.

    Поэтому просьба, поделиться "лайф-хаками", когда использование исключений и последующего "разворачивания стека" - делает код близким к идеалу. Просто несистемные примеры, потом разберёмся. Кстати. Если в том же С++ есть участки кода без задействования "механизмов" Qt - у меня есть полное и честное право использовать обработку исключений.

    Кстати да!

    Есть две, скажем так, парадигмы, которые меня очень интересуют в разработке - "Шаблоны проектирования от банды четырёх" и принципы "S.O.L.I.D". И если у вас получатся примеры лайфхаков в разрезе этих доктрин - мое к вам уважение на этот месяц превысит все допустимые пределы!!! :blink:
      Методика обработки ошибок слишком комплексна, чтобы можно было определить чёткие критерии выбора той или иной. Самый простой критерий якак-то прочитал... вот не помню, наверное Саттера, но это неточно, и он гласит, что исключения обязательно должны использоваться, когда нет других способов сообщить об ошибке и предотвратить выполнение потока исполнения, кроме как вернуться к предыдущему, гарантировано безопасному, состоянию. В этом смысле исключения являются развитием парадигмы транзакционной целостности исполнительного окружения. Ну т.е. просто мы помечаем некое место в коде, к которому можно безопасно откатиться в случае, если транзакцию следует откатить, а не подтвердить. В остальных случаях использование исключений следует минимизировать, т.к. они по своей природе неструктурны, что противоречит парадигме структурного программирования. Типичный пример, когда они не нужны – валидация введённых пользователем данных. Типичный пример, когда нужны – подсистема не предусматривает того, что запрошенный контракт может быть не выполнен. Как вариант: интерфейс подсистемы может быть перепроектирован, но это сложно, неудобно или ещё чего. Вот std::vector<>::at() возвращает просто ссылку, и это удобно. В случае если возвращать нечего, нет никаких других вариантов, кроме исключения. Можно перепроектировать, чтобы был, но это будет жутко неудобно. К тому же есть альтернативный вариант в явной проверке перед вызовом std::vector<>::at(), который в этом случае выгоднее заменить на std::vector<>::operator[], поэтому перепроектирование в целом сделает только хуже.
      Но минимизировать не значит отказаться, как и возврат к безопасному состоянию может быть осуществлён не только исключениями. Самый важный + от исключений — автоматическое, гарантированное языком, разрушение всех объектов со временем жизни в диапазоне от точки безопасного отката до обнаружения отказа. Это позволяет настраивать методику отката по специфическим конкретно для этого места в коде потребностям.
        Цитата Qraizer @
        В этом смысле исключения являются развитием парадигмы транзакционной целостности исполнительного окружения.

        Да, похоже это самое главное преимущество. Когда, в случае исключительных ситуаций, все цепочки деаллокаций и разрушений временных объектов как-бы прячутся "под капот".
          Ну, не деаллокаций всё ж. Скажем так, функций очистки. Где-то это free() или operator delete, где-то socketclose(), где-то CloseHandle(), а где-то RollbackTransaction(). Та неважно, что в деструктор впихнёшь, то и исполнится. Аллокации сами по себе, если я понял о чём ты, создают объекты с временем жизни, выходящим за рамки структуры кода, поэтому какой-нибудь malloc() или operator new не будут отменены, для этого требуются объекты, владеющие этими аллокациями. Любые контейнеры, например, таковыми являются, так что std::vector<> или даже std::list<> вполне себе очистятся. На худой конец есть std::shared_ptr<> или std::unique_ptr<>. Просто перестаём пользоваться сырыми указателями, и смеёмся над тупыми властями тупых государств, где работают тупые программеры.

          Добавлено
          Я бы сказал, что исключения являются более безопасной к ошибкам парадигмой, т.к. заставляет программистов обрабатывать отказы. Ибо один из самых главных врагов тут – лень. Вот не проверил состояние ошибки, и всё, код пошёл в разнос. Где-то потом упадёт, ищи-свищи, откуда оно взялось, где-то UB, ещё "лучше". Если вообще упадёт, а то и годами кое-как на соплях работать будет. Конечно, нынче можно завести std::optional<> или там template <typename ...Args> using /*...*/ = std::variant<Args..., std::error_code>, но оба варианта всё равно предполагают, что программер будет проверять, что там лежит. Да, если не проверит и заюзает отсутствующий результат, получит исключение, но даже так это будет просто bool, весь контекст будет утерян. Но код хотя бы стопнется до того, как делов наворотит. Явно заюзав исключения, ты и контекст туда впихнуть сможешь, и либо заставишь программистов их перехватить, чтоб обработать, либо оно само вовремя упадёт, как в случае std::optional<>, так что хуже не будет. А пользователи потом таких горемык закидают тапками в саппорте.
          За это приходится платить. Производительность исключений может пессимизировать код довольно сильно. Тут уж надо смотреть, важнее ли надёжность производительности. Обычно, скорость отклика UI или сетевого взаимодействия это позволяют, но бывают нюансы.
          Сообщение отредактировано: Qraizer -
            Цитата Majestio @
            Поэтому просьба, поделиться "лайф-хаками", когда использование исключений и последующего "разворачивания стека" - делает код близким к идеалу.

            Рихтер. "Создание эффективных приложений...".
            Глава 24.
            Читаем, по результатам пишем процедуру-фильтр.
            Которая выводит контекст, код исключения, имя файла и номер
            строки, где поймано исключение. Можно ещё отрезок двоичных
            кодов программы, вокруг места с исключением.
            (Неплохо также версию OC и версию, билд приложения).
            Всё это выводим в интерфейс вывода. Чтобы легко назначать
            вывод куда захочется в зависимости от конкретного приложения
            и других обстоятельств.
            Скрытый текст

            Например, у меня некоторое количество юзеров с сетевым приложением,
            я их даже всех не знаю. Как узнать ? Данные исключения выводятся
            мне на UDP-сервер.

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

            Добавлено
            Цитата Majestio @
            Есть две, скажем так, парадигмы, которые меня очень интересуют в разработке - "Шаблоны проектирования от банды четырёх"

            Посмотри тут
            Сообщение отредактировано: ЫукпШ -
              Цитата Qraizer @
              Ну, не деаллокаций всё ж. Скажем так, функций очистки. Где-то это free() или operator delete, где-то socketclose(), где-то CloseHandle(), а где-то RollbackTransaction(). Та неважно, что в деструктор впихнёшь, то и исполнится. Аллокации сами по себе, если я понял о чём ты, создают объекты с временем жизни, выходящим за рамки структуры кода, поэтому какой-нибудь malloc() или operator new не будут отменены, для этого требуются объекты, владеющие этими аллокациями. Любые контейнеры, например, таковыми являются, так что std::vector<> или даже std::list<> вполне себе очистятся. На худой конец есть std::shared_ptr<> или std::unique_ptr<>. Просто перестаём пользоваться сырыми указателями, и смеёмся над тупыми властями тупых государств, где работают тупые программеры.

              Хе-хе :lol: я примерно это и имел ввиду. Хотя возможно использовал не общепринятую терминологию. Под разрушением временных объектов - я понимаю вызов их деструкторов с деаллокацией всего временного по иерархии. А под деаллокацией - освобождение любого рода ресурсов, которые были вызваны в конструкторе/головной программе/служебных функциях. Ну, в общем, ладно - думаю мы поняли друг друга.

              Цитата Qraizer @
              Просто перестаём пользоваться сырыми указателями

              В обычной жысти С++ прогера они не сильно востребованы, если не совсем. Но иногда же хочется написать аллокатор на сырых указателях для оператора new для последующего интереса чего там настроилось? Признайся! ;)

              Цитата ЫукпШ @
              Читаем, по результатам пишем процедуру-фильтр.
              Которая выводит контекст, код исключения, имя файла и номер
              строки, где поймано исключение.

              Кстати, да - тоже отличное применение! Всегда завидовал Жаба-прогерам в этом плане. У них когда прога падает - они "искаропки" получают весь стек вызовов. Обязательно почитаю Главу 24, возможно получится такой же стек замутить самому для С++.

              Цитата ЫукпШ @
              Посмотри тут

              Хороший ресурс. Именно по нему, в большей части, я делал для себя свою шпаргалку. И практически во всех случаях там оставлял соответствующую ссылку на этот ресурс. За исключением одного шаблона проектирования "Интерпретатор". Тамошние спецы не считают его "кирпичиком" ввиду большей сложности в реализации.
                Цитата Majestio @
                Обязательно почитаю Главу 24, возможно получится такой же стек замутить самому для С++.
                std::stacktrace
                Получим что-то типа
                ExpandedWrap disabled
                  There are some tests for different functionality units:
                  censored 1.1 лев к
                  censored 1.1 лев м
                  censored 2.1 лев к
                  Stack trace -->:
                  censored\certGenUni\test.cpp, line 557: certGen!fillTests+0x9F2
                  censored\certGenUni\main.cpp, line 38: certGen!main+0xD5
                  D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, line 78: certGen!invoke_main+0x33
                  D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, line 288: certGen!__scrt_common_main_seh+0x15A
                  D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, line 331: certGen!__scrt_common_main+0xD
                  D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp, line 17: certGen!mainCRTStartup+0x8
                  , line 0: KERNEL32!BaseThreadInitThunk+0x19
                  , line 0: ntdll!RtlGetAppContainerNamedObjectPath+0x11E
                  , line 0: ntdll!RtlGetAppContainerNamedObjectPath+0xEE
                Но есть нюанс, по крайней мере в MS VS Studio: нужно наличие отладочной инфы. Без оной вывод будет куда скромнее:
                ExpandedWrap disabled
                  There are some tests for different functionality units:
                  censored 1.1 лев к
                  censored 1.1 лев м
                  censored 2.1 лев к
                  Stack trace -->:
                  , line 0: certGen+0x301E9
                  , line 0: certGen+0xC58D2
                  , line 0: certGen+0xAB775
                  , line 0: certGen+0xFAAB3
                  , line 0: certGen+0xFA92A
                  , line 0: certGen+0xFA7BD
                  , line 0: certGen+0xFAB38
                  , line 0: KERNEL32!BaseThreadInitThunk+0x19
                  , line 0: ntdll!RtlGetAppContainerNamedObjectPath+0x11E
                  , line 0: ntdll!RtlGetAppContainerNamedObjectPath+0xEE
                  А все равно line 0
                    Qraizer, o5 плюсую :lol:

                    Цитата macomics @
                    А все равно line 0

                    Ну, думаю, тут всё будет зависеть от твоей/своей политики лицензирования ... Билды а-ля LTS конечно можно избавить от дебаг-шлака. Но всякие там релиз-кандидаты ... а почему бы и нет?!!! Я так понимаю, что политику лицензирования каждый пипл волен сам выбирать для себя, равно как и декларацию "качества" предоставляемого программного продукта.
                      Подозреваю, если закинуть виндовые символы, то и там можно получить вменяемые буковки и циферки. Только толку с этого, коли сырцов всё равно нет
                        Цитата Qraizer @
                        Подозреваю, если закинуть виндовые символы, то и там можно получить вменяемые буковки и циферки. Только толку с этого, коли сырцов всё равно нет

                        Такую тему, я щетаю, нужно выставлять на конкурс сырцов. С призовым фондом минимум 2000Dgm 8-)
                          Впрочем, при желании легко отсеять не относящиеся к своему коду строчки внизу отчёта:
                          ExpandedWrap disabled
                            IDispatch's error: IDispatch::GetIDsOfNames("TablesOfContents") failed w/err 0x80010001
                            PropCreator(): src is not an IDispatch: -2147467262 (80004002)
                            Stack trace -->:
                            censored\certGenUni\property.h, line 32: certGen!Property::Property+0x9D
                            censored\certGenUni\cases.cpp, line 1037: certGen!`makeReports'::`2'::<lambda_5>::operator()+0x142
                            censored\certGenUni\cases.cpp, line 1059: certGen!`makeReports'::`2'::<lambda_6>::operator()+0xA15
                            censored\certGenUni\cases.cpp, line 1102: certGen!makeReports+0x13DA
                            censored\certGenUni\main.cpp, line 106: certGen!main+0x59B
                            Qraizer, плис, накидай академический пример. Так, чтобы виден был последовательный вызов/создание и такой же вызов уничтожения, ну и сам трасерт вызовов. Тематика вааще пофик. Главное - сам пример.
                              Та чё там накидывать... Прям из certGen возьму.
                              ExpandedWrap disabled
                                class StackTrace: public std::runtime_error
                                {
                                  std::stacktrace stack;
                                 
                                public:
                                  explicit StackTrace(const std::string& msg): std::runtime_error(msg),
                                                                               stack(std::stacktrace::current()) {}
                                  const std::stacktrace& getStack() const throw() { return stack; }
                                  void report(std::ostream& os) const;
                                };
                                 
                                // Класс общего исключения COM. Хранит HRESULT помимо текста.
                                class COM_Error: public StackTrace
                                {
                                  HRESULT code;
                                 
                                public:
                                  explicit COM_Error(HRESULT err):                         StackTrace("A COM error is occured"), code(err) {}
                                           COM_Error(HRESULT err, const std::string& msg): StackTrace(msg),                      code(err) {}
                                          ~COM_Error()throw() {}
                                 
                                  HRESULT why() const throw() { return code; }
                                };
                                 
                                // Класс исключения OLE. Основан на общих исключениях COM.
                                class OLE_Error: public COM_Error
                                {
                                public:
                                  explicit OLE_Error(HRESULT err):                         COM_Error(err, "An OLE error is occured") {}
                                           OLE_Error(HRESULT err, const std::string& msg): COM_Error(err, msg) {}
                                          ~OLE_Error() throw() {}
                                };
                                 
                                /* тут не заморачиваемся параметризацией типами символов, аллокаторами итп, т.к. при размотке стека по исключению
                                   всякие там narrow <=> wide конверсии ...ну их на всякий случай; просто юзаем std::ostream и std::string */
                                void StackTrace::report(std::ostream& os) const
                                {
                                  static const std::string StackTraceClass= "StackTrace";
                                  static const std::string COM_ErrorClass = "COM_Error";
                                  static const std::string OLE_ErrorClass = "OLE_Error";
                                 
                                  for (const auto& entry : stack)
                                    // срежем само создание исключения с верхушки стека
                                    if (entry.description().find(StackTraceClass)== std::string::npos &&
                                        entry.description().find(COM_ErrorClass) == std::string::npos &&
                                        entry.description().find(OLE_ErrorClass) == std::string::npos)  // <= допишите свои фильтры по желанию
                                      os << entry.source_file() << ", line " << entry.source_line() << ": "
                                         << entry.description() << '\n';
                                  os.flush();
                                }
                                 
                                /* пример использования; всё как обычно, ничего особенного */
                                struct PropCreator
                                {
                                  static IDispatch* create(const Variant& src)
                                  { // создавать свойства на Variant-ах, которые не IDispatch, мы не умеем.
                                    if (src.vt != VT_DISPATCH) throw OLE_Error(E_NOINTERFACE, "PropCreator(): src is not an IDispatch");
                                    return src.pdispVal;
                                  }
                                };
                                 
                                /* ... */
                                  try
                                  {
                                    /* ... */
                                  }
                                  catch(const COM_Error& exc)
                                  {
                                    std::clog << exc.what() << ": " << exc.why() << " ("
                                              << std::hex << std::uppercase << exc.why() << ")\nStack trace -->:"
                                              << std::dec << std::endl;
                                    exc.report(std::clog);
                                    return EXIT_FAILURE;
                                  }
                                  catch(const StackTrace& exc)
                                  {
                                    std::clog << exc.what() << "\nStack trace -->:\n";
                                    exc.report(std::clog);
                                    return EXIT_FAILURE;
                                  }
                              Сообщение отредактировано: Qraizer -
                                Нет. Твой пример некрасивый и не полный. Сорян!

                                Давай ты решишь (в рамках сабжа!!!) другую задачу:

                                1) Задается произвольная матрица целых чисел размерностью 3x3
                                2) Одно из чисел (пусть одно) получит значение "нуль".
                                3) Нужно получить матрицу-результат = каждое значение исходной матрицы деленное на минимальное значение чисел из матрицы (что явно не есть нуль, но быть может)

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

                                Вот это, Qraizer, будет зе бест фром зе уэст! Остальные твои:

                                ExpandedWrap disabled
                                  try
                                    {
                                      /* ... */
                                    }

                                Кагбэ намекают - слишком тонко для обычного пипла.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0469 ]   [ 16 queries used ]   [ Generated: 5.12.24, 16:04 GMT ]