
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.75] |
![]() |
|
Страницы: (6) « Первая ... 2 3 [4] 5 6 все ( Перейти к последнему сообщению ) |
Сообщ.
#46
,
|
|
|
Ну а по теме ... запилил две синтетические реализации паттерна "Фасад":
на кодах возврата ![]() ![]() #include <iostream> class SubsystemA { public: int operationA() noexcept { // Имитация успешной операции return 0; // 0 - успех } }; class SubsystemB noexcept { public: int operationB() { // Имитация ошибки return -1; // -1 - ошибка } }; class Facade { private: SubsystemA subsystemA; SubsystemB subsystemB; public: int performOperations() noexcept { if (subsystemA.operationA() != 0) { return -1; // Ошибка в A } if (subsystemB.operationB() != 0) { return -2; // Ошибка в B } return 0; // Успех } }; int main() { Facade facade; int result = facade.performOperations(); if (result == 0) { std::cout << "The operation was successful." << std::endl; } else { std::cout << "An error occurred: " << result << std::endl; } return 0; } на обработках исключений ![]() ![]() #include <iostream> #include <stdexcept> class SubsystemA { public: void operationA() { // Имитация успешной операции // Ничего не происходит } }; class SubsystemB { public: void operationB() { // Имитация ошибки throw std::runtime_error("Error in SubsystemB"); } }; class Facade { private: SubsystemA subsystemA; SubsystemB subsystemB; public: void performOperations() { subsystemA.operationA(); // Успех subsystemB.operationB(); // Может выбросить исключение } }; int main() { Facade facade; try { facade.performOperations(); std::cout << "The operation was successful." << std::endl; } catch (const std::runtime_error& e) { std::cout << "An error occurred: " << e.what() << std::endl; } return 0; } Благодаря тому, что оба варианта примитивно-простые, особого преимущества второго варианта перед первым я не наблюдаю. По компактности и читаемости оба варианта примерно равны. Вопрос знатокам ![]() Какие можно внести правки для усложнения, чтобы показать, что вариант с обработкой исключений стал более красивым (компактным, читаемым, эффективным) по сравнению с вариантом на кодах возврата? Да и, собственно, просьба - внесите и покажите! |
Сообщ.
#47
,
|
|
|
Цитата Majestio @ Какие можно внести правки для усложнения, чтобы показать, что вариант с обработкой исключений стал более красивым (компактным, читаемым, эффективным) по сравнению с вариантом на кодах возврата? Да и, собственно, просьба - внесите и покажите! 1) Увеличение глубины вложенности классов. Как в виде наследования, так и в виде просто дополнительных объектов классов. (Это к удобству использования второго варианта) 2) Варианты исправления ошибки и перезапуска операции (допустим ошибка сигнализирует о timeout/retry). (Это к применимости первого варианта) 3) Как в первом случае найти точное место возникновения ошибки (не к минусам, а к необходимости усложнения обработки: не равноценные примеры)? |
Сообщ.
#48
,
|
|
|
Цитата macomics @ 3) Как в первом случае найти точное место возникновения ошибки (не к минусам, а к необходимости усложнения обработки: не равноценные примеры)? Ну тут просто. Создается enum со всеми видами ошибок. Другое дело, если один и тот же вид ошибок может быть в разных местах. Но и это решается - вместо числового кода возврата возвращается структура, включающая сам код, и макро __FILE__, __LINE__. Что-то типа: ![]() ![]() #include <iostream> #include <optional> #include <string> struct ErrorInfo { int errorCode; std::string fileName; int lineNumber; }; std::optional<ErrorInfo> performOperation(bool shouldFail) { if (shouldFail) { return ErrorInfo{404, __FILE__, __LINE__}; // Код ошибки, имя файла и номер строки } return std::nullopt; // Успешное выполнение, ошибки нет } int main() { auto result = performOperation(true); // Изменяем на false для успешного выполнения if (result) { std::cout << "Error: Code " << result->errorCode << ", File: " << result->fileName << ", Line: " << result->lineNumber << std::endl; } else { std::cout << "The operation was successful." << std::endl; } return 0; } Другое дело, что без стека вызовов, тут часто будет недостаточно инфы для однозначного определения причины. Но это уже другая тема. В принципе это же касается и варианта с использованием исключений, а-ля: ![]() ![]() #include <iostream> #include <optional> #include <string> #include <stdexcept> struct ErrorInfo { int code; std::string file; int line; }; class CustomException : public std::runtime_error { public: CustomException(const std::string& message, const std::string& file, int line) : std::runtime_error(message), errorInfo{1, file, line} {} const ErrorInfo& getErrorInfo() const { return errorInfo; } private: ErrorInfo errorInfo; }; std::optional<ErrorInfo> riskyOperation() { // Симулируем ошибку и выбрасываем исключение с информацией о месте выброса throw CustomException("Something went wrong!", __FILE__, __LINE__); } int main() { try { riskyOperation(); } catch (const CustomException& e) { // Обработка ошибки std::optional<ErrorInfo> error = e.getErrorInfo(); if (error) { std::cout << "Error Code: " << error->code; std::cout << ", File: " << error->file; std::cout << ", Line: " << error->line; std::cout << ", Message: " << e.what() << std::endl; } } return 0; } |
![]() |
Сообщ.
#49
,
|
|
Цитата Majestio @ А оно надо? Ты ж не забывай о главном правиле дизайна библиотек: если нет никаких причин предпочитать одно решение остальным, его следует отдать на откуп пользователям библиотеки. Какие можно внести правки для усложнения, чтобы показать, что вариант с обработкой исключений стал более красивым (компактным, читаемым, эффективным) по сравнению с вариантом на кодах возврата? Да и, собственно, просьба - внесите и покажите! |
Сообщ.
#50
,
|
|
|
Цитата Qraizer @ А оно надо? Надо! Мы тут синтетические примеры решаем. И пытаемся (одно из) определить почему Qt почти отказались от обработки исключений в угоду другим способам обработки ошибок. Цитата Qraizer @ А оно надо? И второе, что О-Ч-Е-Н-Ь надо ... таки решить или обозначить тему топика. Где когда и что лучше?!! Дружище, давай "разговаривать и аргументировать" кодом. Пустые слова тут мало-мало ничего не решают по вопросу. Цитата Qraizer @ на откуп пользователям библиотеки А нам, пользователям библиотек, не безразлично! Особенно простой интерес - откуда у вас ноги растут, и почему оттуда? :-) |
Сообщ.
#51
,
|
|
|
Цитата Majestio @ Ну тут просто. Создается enum со всеми видами ошибок. Кажется, что ты опять игнорируешь std::expected ![]() Добавлено Цитата Majestio @ И пытаемся (одно из) определить почему Qt почти отказались от обработки исключений в угоду другим способам обработки ошибок. Этому решению очень много лет. Тогда исключения были достаточно медленными и мне кажется плохо ложились на дизайн qt и его кодогенрацию и объектную модель. Т.е. считай исторические причины. ИМХО. Добавлено Цитата Qraizer @ Ты ж не забывай о главном правиле дизайна библиотек: если нет никаких причин предпочитать одно решение остальным, его следует отдать на откуп пользователям библиотеки. Ну да, можно пойти по пути asio с его функциями (для синхронного API), которые принимают опционально ссылку на код ошибки, а если не передаешь, то будет исключение. |
Сообщ.
#52
,
|
|
|
Цитата D_KEY @ Кажется, что ты опять игнорируешь std::expected Он таки получше std::optional будет для подобного. Ты прав. Я пока не дошёл до чтения фич std::expected. Но я исправлюсь, скорее всего завтра. Цитата D_KEY @ Этому решению очень много лет. Тогда исключения были достаточно медленными и мне кажется плохо ложились на дизайн qt и его кодогенрацию и объектную модель. Т.е. считай исторические причины. ИМХО. Похоже на то. Если я не ошибаюсь? Qt свой старт начал даже задолго до C++11. Т.е. еще на "ламповой" версии С++. |
Сообщ.
#53
,
|
|
|
Цитата D_KEY @ std::expected Мой первый блин комом - статья на Хабре. Читал сперва вдумчиво. Потом, в какой-то момент, мысль "поплыла", потому как параллельно возникла вторая - "начали городить огород". Дальше читал еще чуть бегло. Ну потом как обычно, какие-то лешие вышли из леса, русалки на ветвях уселись... Шютка ![]() ![]() Любая проектируемая система обладает своим набором уникальных состояний. Соответственно обладает своим набором и типовых ошибок. Да, часть множества ошибок и состояний может пересекаться от системе-к-системе. По типу "файл не найден", "ошибка записи" & etc. Но это не означает их идентичность! Везде же есть свой контекст. Что чел хотел сделать в статье, а тем более где-то там в глубинах статьи он еще присобачил динамический полиморфизм! ![]() ![]() Возможно мой "старт" стартанул некузяво. Но такая кухня мне явно не надо! Мой принцип давно и надолго - Бритва Оккама. А тут ни бритвы не видно, и Оккама давно не сидел. ... попробую поискать что-то более вменяемое ![]() |
![]() |
Сообщ.
#54
,
|
|
Ну, человек поставил себе задачу, человек её решил и поделился решением. Решение наиуниверсальнейшее, так что почему бы и нет. Я бы вот не стал настолько заморачиваться, но положа руку на сердце, ведь именно за то мы и любим Плюсы, что можем наваять либу так, что пользоваться ею можно будет так же, как будто это языковое средство.
Скажем так: если тебе интересно, как оно устроено, то любая либа внутри выглядит страшно, но если тебе интересно лишь, как оно работает, то юзаешь и не паришься. |
Сообщ.
#55
,
|
|
|
А тебе не кажется, что это немотивированное усложнение гораздо простого вопроса? Ну если честно, Только если честно?
Добавлено Цитата Qraizer @ Решение наиуниверсальнейшее, так что почему бы и нет. Стоп! Ты тоже считаешь - что ошибки нужно типизировать? И что это реально даст профит?!!! ![]() |
Сообщ.
#56
,
|
|
|
Majestio, ну первый же пример из статьи понятен?
![]() ![]() enum class MathError : unsigned char { ZeroDivision, NegativeNotAllowed }; std::expected<int, MathError> Bar(int a, int b) { if (b == 0) return std::unexpected(MathError::ZeroDivision); if (a < 0 || b < 0) return std::unexpected(MathError::NegativeNotAllowed); return a / b; } Существенная разница с optional тут в том, что ты не забываешь вид ошибки. Это близко к Rust'овскому Result<T, E>. А дальше он в статье о чем-то другом уже начал ![]() |
Сообщ.
#57
,
|
|
|
Цитата D_KEY @ Существенная разница с optional тут в том, что ты не забываешь вид ошибки. Я вид/тип ошибки могу успешно не забывать и кодом ошибки? И это считаю (пока) самым простым и изящным решением. Убеди меня что мне ну вот очень-очень нужно ошибки "типизировать" ![]() ![]() |
Сообщ.
#58
,
|
|
|
Цитата Majestio @ Я вид/тип ошибки могу успешно не забывать и кодом ошибки? В optional не сможешь ![]() Цитата Majestio @ Убеди меня что мне ну вот очень-очень нужно ошибки "типизировать" ![]() ![]() Ну можешь использовать std::expected<T, int> и использовать свои int'овые коды на здоровье ![]() Суть-то в том, что "обычный" код ошибки ты можешь проигнорировать случайно и т.п., а тут придется обработать. Собственно, мотивация Result<T, E> в Rust такая же. |
Сообщ.
#59
,
|
|
|
Цитата D_KEY @ что ты не забываешь вид ошибки Искусственное усложнение, детектед! Есть набор ошибок - этого для обработки хватает. А вот для анализа ... но это уже потом ... собирай их в группы как хочется. В свои эти "виды". Но зачем эту шляпу внедрять в код? Хватит номеров ошибок, не? Добавлено Цитата D_KEY @ Суть-то в том, что "обычный" код ошибки ты можешь проигнорировать случайно и т.п., а тут придется обработать. Собственно, мотивация Result<T, E> в Rust такая же. Т.е. я объявляю секцию catch или if-фю код возврата и ... сразу "случайно" забываю нахрена я это делаю?!! ![]() |
Сообщ.
#60
,
|
|
|
Цитата Majestio @ или if-фю код возврата или не объявляешь ![]() Добавлено И я тебя не уговариваю, используй что хочешь. Страдать тебе потом самому ![]() |