Russian Qt Forum
Ноябрь 05, 2024, 22:13 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 [3] 4 5   Вниз
  Печать  
Автор Тема: C++ Exceptions и Qt  (Прочитано 34698 раз)
Bepec
Гость
« Ответ #30 : Апрель 18, 2012, 08:33 »

Akon - я тебе ещё про один альтернативный вид ветвления расскажу - goto. Попробуй, тебе понравится!!!

Цитировать
1. Хоть сколько нибудь серьезная программа никогда не будет размещать бизнес логику (Validate) в слое GUI.
А логика и gui в Qt разбиты на ui класса и, собственно, сам класс. Почитайте что-ли книжки, а?

Цитировать
2. В слое бизнес логики вы не можете использовать if (!Validate()) return ShowError(..); поскольку ShowError - это конкретное действие, допустим MessageBox. А если консольная прога? Там не нужен MessageBox. Получается, что бизнес логика зависит от типа приложения. Да, можно абстрагироваться от конкретного вывода ошибки, но получается дополнительная зависимость бизнес логики, которой нет в случае исключений.
Какой нафиг слой бизнес логики? А вы в исключении хотите не конкретное действие??? Вы нейронную сеть что-ли написали и посадили за обработку ошибок??? Чтобы каждое исключение  выполняло не конкретный код(предупреждение, варнинг, ошибку, ИСКЛЮЧЕНИЕ, подрыв заряда тротила), а формировало совершенно новый код, непредсказуемым образом исправляющий ошибку?

И каким фигом, простите уж, вы скрещиваете валидацию GUI и консоли?

Вам что, на обед подают чай в супе, приправленный селёдкой и залитый сверху литром молочка? Не мешайте GUI и консоль. Они вообще то разные.

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

Кхм. Опять таки вы мешаете валидацию (проверку на правильность параметров) и ошибки. Походу это болезнь...
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #31 : Апрель 18, 2012, 09:39 »

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

1. Хоть сколько нибудь серьезная программа никогда не будет размещать бизнес логику (Validate) в слое GUI.
2. В слое бизнес логики вы не можете использовать if (!Validate()) return ShowError(..); поскольку ShowError - это конкретное действие, допустим MessageBox. А если консольная прога? Там не нужен MessageBox. Получается, что бизнес логика зависит от типа приложения. Да, можно абстрагироваться от конкретного вывода ошибки, но получается дополнительная зависимость бизнес логики, которой нет в случае исключений.
Вместо раздражающего термина "бизнес логика" (напоминает "бизнес леди") лучше сказать проще: расчетная часть не должна зависеть от UI. С этим никто не спорит, но почему обязательно использовать исключения для развязки? Теоретически прыжок исключения вроде бы то что надо, но..

Вы влепили try/catch в перекрытый nofify. (больше некуда). Но ведь notify - это чисто дела UI. Более того, это ловля всех исключений, а совсем не испущенных одним диалогом. Переделать этот код в консольное приложение стало сложнее.

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

3. Нет механизма структурированной обработки ошибки (в нетривиальной бизнес логике это важнейший аспект).
То есть "наша ошибка" - обрабатываем, не наша - throw, пусть наверху разбираются (поправьте если не так). Это дело хорошее, но какая в этом необходимость для диалоге ввода? Очень может быть что и никакой.

Пока мое впечатление: теория применяется механически, без всякого учета особенностей задачи. Типа "исключения - круто, значит будем их юзать"  Улыбающийся
Записан
silart
Гость
« Ответ #32 : Апрель 18, 2012, 10:43 »

Спасибо всем за высказанные мнения!  Подмигивающий

Хотелось бы еще обратиться к товарищу Akon'у. QApplication::notify() вызывается на каждое событие в очереди? То есть получается что через notify() пропускается каждое событие?
Получается что такой подход с notify() позволяет установить обработчик исключений на все приложение целиком. Это все хорошо, но было бы еще лучше, если бы у класса QDialog был бы свой notify().  Улыбающийся К сожалению так не получается.

Еще вопрос. Вы говорили про отделение слоя бизнес-логики от слоя GUI. Хотелось бы взглянуть на такой подход на практике. Не могли бы вы показать код небольшого класса, где используется такой подход.
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #33 : Апрель 18, 2012, 12:27 »

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

Но к сожалению Qt не позволяет полноценно работать с исключениями. Я сперва использовал метод с перегрузкой QApplication::notify() ставил там try/catch и показывал диалог ошибки. Все работало, пока мне не стали попадаться дистрибутивы с криво собранным Qt (altlinux, mcbc), в них возникающее исключение никак не отлавливалось, проходя через потроха Qt и тупо звало abort(). Не помогли никакие пляски с бубном и опциями компилятора.

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

Для удобства написал макрос: SUPPRESS_EXCEPTIONS, который исключение глушит и логгирует. И везде его растыкиваю.

примерно так:
Код
C++ (Qt)
void MyObject::mySlot()
{
   try
   {
       ....
   }
   SUPPRESS_EXCEPTIONS
}
« Последнее редактирование: Апрель 18, 2012, 12:29 от navrocky » Записан

Гугль в помощь
Bepec
Гость
« Ответ #34 : Апрель 18, 2012, 12:57 »

Мда... Исключения, которые ловятся не всегда и не везде... Использовать их везде...

Мде... Ну ваш код в ваших руках. Но здравомыслящее большинство считает исключения именно исключающимися ситуациями Подмигивающий
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #35 : Апрель 18, 2012, 13:35 »

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

Я ни в коей мере не возражаю против такого подхода, и сам его использую. Просто "все то же самое" столь же справедливо как со знаком плюс так и минус. Напр отказ от исключений тоже может себя прекрасно оправдать, о чем говорит хотя бы стиль Qt. А подвести "теоретическую базу" можно с равным успехом - под противоположные выводы  Улыбающийся 

Еще вопрос. Вы говорили про отделение слоя бизнес-логики от слоя GUI. Хотелось бы взглянуть на такой подход на практике. Не могли бы вы показать код небольшого класса, где используется такой подход.
На "небольшом классе" все вероятно будет прекрасно. Но вот у Вас есть какой-то проект с UI (детали неважны). Теперь представьте себе что Вы получили директиву (или заказ) - задача должна эксплуатироваться с командной строкой и не выводить никаких окон.

Подумайте что Вам понадобится для такой переделки - и Вы найдете очень много интересного. Это за спиной мощного инструментария легко калякать об "архитектуре" - а дойдет до дела - так "мама не горюй"  Улыбающийся
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #36 : Апрель 18, 2012, 14:25 »

Мда... Исключения, которые ловятся не всегда и не везде... Использовать их везде...

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

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

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

Цитировать
Напр отказ от исключений тоже может себя прекрасно оправдать, о чем говорит хотя бы стиль Qt.

Ну да, при этом надо скурпулезно проверять возвращаемое значение каждого метода. Иначе программа будет просто молча не работать.

Например, открыли файл - проверили, написали код для обработки ошибки, затем прочли N байт - проверили что прочли N байт, написали код для случая, когда не прочли. и т.д. В итоге получается бООльшая простыня из кучи проверок.

С исключениями этого всего просто нет. Все проще.
Записан

Гугль в помощь
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #37 : Апрель 18, 2012, 14:42 »

Например, открыли файл - проверили, написали код для обработки ошибки, затем прочли N байт - проверили что прочли N байт, написали код для случая, когда не прочли. и т.д. В итоге получается бООльшая простыня из кучи проверок.
Ну а диалог ввода-то здесь причем? Улыбающийся Приведя один пример Вы чего-то обобщаете его на все случаи жизни. Хотя не спорю, пример удачный, здесь "проверять после каждого" нереально.

Записан
Akon
Гость
« Ответ #38 : Апрель 18, 2012, 14:50 »

Igors:
Цитировать
Не то чтобы я негативно к ним отношусь, но у меня в одном проекте их переизбыток. Т.е. исключения используются даже там где, на мой взгляд, никакой необходимости в них нет....
Ну так это просто драма этого проекта (а теперь и ваша личная Улыбающийся)

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

Цитировать
Вы влепили try/catch в перекрытый nofify. (больше некуда). Но ведь notify - это чисто дела UI. Более того, это ловля всех исключений, а совсем не испущенных одним диалогом. Переделать этот код в консольное приложение стало сложнее.
notify - это не UI! Это QCoreApplication::notify!

Представьте себя архитектором фрэймворка, например, такого как Qt. В фрэймворке используется событийная модель для коммуникации компонентов: события извлекаются из очереди, диспетчирзируются и обрабатываются. Пусть для сигнализации об ошибках используются исключения. Далее, вы задаетесь вопросом самой общей обработки ошибок, так сказать обработкой ошибок по-умолчанию или необработанных ошибок. Мне сложно представить архитектора, которому не пришло бы в голову обеспечить такую обработку; в случае исключений - заключить цикл выборки сообщений в блок try/catch и обеспечить в нем обработку по-умолчанию. В GUI программах обработка по-умолчанию, как правило, сводится к показу сообщения пользователю, при этом сам текст сообщения формируется локально в месте ошибки. Если рассмотреть аспект ввода данных в диалог, то упомянутая мной обработка по-умолчанию - это все что нужно! Конечно, если требуется специальная обработка, то вы выполните ее и прекратите распространение исключения (раскрутку стека).

Сами тролли это все учли, поэтому и предоставили возможность вмешаться в процесс и даже предоставили варнинг-инструкцию. Тем не менее, исключения не используются Qt по причинам снижения универсальности фрэймворка.

Цитировать
3. Нет механизма структурированной обработки ошибки (в нетривиальной бизнес логике это важнейший аспект).
То есть "наша ошибка" - обрабатываем, не наша - throw, пусть наверху разбираются (поправьте если не так). Это дело хорошее, но какая в этом необходимость для диалоге ввода? Очень может быть что и никакой.
Да. Структурированная - значит обработка (при необходимости) на каждом уровне. Например, более низкоуровневые исключения могут гаситься, а вместо них генерироваться более высокоуровневые (хороший дизайн предусматривает, что высокоуровневое исключение будет иметь информацию о вызвавшем ее более низкоуровневом исключении (.Net: Exception::InnerException)).
Код:
void connectToDataBase()
{
    try {
        connect();
    }
    catch (const NetworkError& e) {
        // здесь выполняем локальную обработку
        throw DataBaseConnectionError(e.what());          
    }
}

Цитировать
Пока мое впечатление: теория применяется механически, без всякого учета особенностей задачи. Типа "исключения - круто, значит будем их юзать"

Ну есть такое, как и во всех других областях. Особенно забавны в этом плане паттернисты.

silart:
Цитировать
Хотелось бы еще обратиться к товарищу Akon'у. QApplication::notify() вызывается на каждое событие в очереди? То есть получается что через notify() пропускается каждое событие?
Получается что такой подход с notify() позволяет установить обработчик исключений на все приложение целиком. Это все хорошо, но было бы еще лучше, если бы у класса QDialog был бы свой notify().   К сожалению так не получается.
QDialog::exec() крутит свой локальный цикл. А цикл сообщений (QEventLoop) диспетчеризирует события через QCoreApplication::notify(). Фактически, QCoreApplication::notify() вызывается из QDialog::exec().

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

Это настолько фундаментальная концепция, что даже не знаю Веселый
Код:
class EvenNumber
{
    int value() const { ... }
    void setValue(int value)
    {
        if (value % 1)
            throw InvalidParameterException(UserReadableAndLocalizedDescrioptionOfTheProglem);
        ....
    }
    void setValue(const QString& value)
    {
        int intValue = convertStringToInt(value);  // throws InvalidConversionError
        setValue(value);
    }
}

class Dialog : public Dialog
{
public:
    Dialog(EventNumber& dataToBeModified) : dataToBeModified_(dataToBeModified) { ... }

private:
    EventNumber& dataToBeModified_;
    void lineEditEditingFinished()
    {
        dataToBeModified_.setValue(lineEdit->text());
    }
};
Записан
Bepec
Гость
« Ответ #39 : Апрель 18, 2012, 15:33 »

Код:
class EvenNumber
{
    int value() const { ... }
    void setValue(int value)
    {
        if (value % 1)
            throw InvalidParameterException(UserReadableAndLocalizedDescrioptionOfTheProglem);
        ....
    }
    void setValue(const QString& value)
    {
        int intValue = convertStringToInt(value);  // throws InvalidConversionError
        setValue(value);
    }
}

class Dialog : public Dialog
{
public:
    Dialog(EventNumber& dataToBeModified) : dataToBeModified_(dataToBeModified) { ... }

private:
    EventNumber& dataToBeModified_;
    void lineEditEditingFinished()
    {
        dataToBeModified_.setValue(lineEdit->text());
    }
};

Чем вот это, отличается от:
Код:
      
if (value % 1)
    emit error();

Ответ: ничем. Но зато есть возможность написать исключение с параметром, и где то далеко наверху его обработать. Что сказать, замечательно Подмигивающий

PS хотя намерения благие, но средства достижения избыточны. ИМХО Подмигивающий
Записан
V1KT0P
Гость
« Ответ #40 : Апрель 18, 2012, 16:16 »

Ну да, при этом надо скурпулезно проверять возвращаемое значение каждого метода. Иначе программа будет просто молча не работать.

Например, открыли файл - проверили, написали код для обработки ошибки, затем прочли N байт - проверили что прочли N байт, написали код для случая, когда не прочли. и т.д. В итоге получается бООльшая простыня из кучи проверок.

С исключениями этого всего просто нет. Все проще.
Ну дык и ловить исключения тоже можно забыть или одно какое-то исключение пропустить и тоже ничего хорошего не будет. Я себе плохо представляю как можно на исключениях строить логику, это-же получится простыня из catch-ей. Выделили память в куче в функции и вызвали другую функцию, она бросила исключения стек свернулся а память в куче так занята и осталась. Исключение это goto, но только не скованный пределом функции.
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #41 : Апрель 18, 2012, 16:27 »

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

Все оттого, что реально их не использовали. У меня один try/catch на самом верху в notify, который рапортует об ошибке (хотя теперь для надежности еще оборачиваю слоты). В остальном я о них не думаю, просто пишу код. От утечек спасают смарт-поинтеры, кутэшное владение QObject и RAII. Стараюсь вообще обычные указатели не использовать. Исключение - хитрые оптимизации, но такого кода у меня очень мало.
Записан

Гугль в помощь
silart
Гость
« Ответ #42 : Апрель 18, 2012, 16:28 »

Цитировать
Чем вот это, отличается от:
Код:
      
if (value % 1)
    emit error();

Ответ: ничем. Но зато есть возможность написать исключение с параметром, и где то далеко наверху его обработать. Что сказать, замечательно Подмигивающий

PS хотя намерения благие, но средства достижения избыточны. ИМХО Подмигивающий

Верес, emit отличается от throw тем, что код после throw, в случае его срабатывания, не исполняется. Когда бросаем throw, начинается раскрутка стека, в результате которой вызываются деструкторы всех созданных в стеке объектов и утечка ресурсов не происходит.
Когда же вы вызываете emit, это приводит к вызову всех слотов, и после этого код стоящий после emit будет выполняться далее.
Записан
silart
Гость
« Ответ #43 : Апрель 18, 2012, 16:31 »

navrocky, в вашем случае, когда у вас перестали ловиться исключения в notify(), разве нельзя было пересобрать Qt?
Вы используете shared или static сборку Qt?
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #44 : Апрель 18, 2012, 16:37 »

navrocky, в вашем случае, когда у вас перестали ловиться исключения в notify(), разве нельзя было пересобрать Qt?
Вы используете shared или static сборку Qt?

Пересобрать к сожалению нельзя. Софт должен использоваться на этих кривых осях, а системные пакеты там переколбашивать нельзя.

Да и есть один аргумент. Если Qt пишется без учета исключений, а прослойка в обработчике евентов там довольно жирная - значит есть довольно большой шанс, что как минимум могут ресурсы утечь, что-то недоинициализируется... Т.к. это недокументированная возможность ловли исключений через QCoreApplication::notify, не исключено, что если сегодня оно не сегфолтится и не течет, то завтра вполне это может случиться.
Записан

Гугль в помощь
Страниц: 1 2 [3] 4 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.131 секунд. Запросов: 23.