Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: silart от Апрель 17, 2012, 05:44



Название: C++ Exceptions и Qt
Отправлено: silart от Апрель 17, 2012, 05:44
Добрый день!

Товарищи! Кто-нибудь использует исключения С++ совместно с библиотекой Qt?

Ситуация такая: Есть некий диалог, который вызывается модально. На нем есть кнопки, поля ввода и другие виджеты. Диалог работает в соответствии со своей логикой. Таким образом, для диалога необходима проверка правильности ввода данных, заполнения полей, и т. д. Было бы очень удобно, если при нажатии на кнопку "ОК", в соответствующем слоте, бросались исключения, если пользователь сделал что-то неправильно:

Код:
void MyDialog::OnButtonOk()
{
    if (...) throw std::logic_error("some error");
}

А при вызове диалога использовалась такая конструкция:
Код:
try
{
   MyDialog dlg(this);
   dlg.exec();
}
catch(const std::exception& e)
{
// Вывод сообщения
}

Однако, такой подход неудобен тем, что при первом исключении сразу уничтожается объект диалога. Допустим пользователь забыл заполнить нужное поле, и после нажатия на "ОК", диалог уничтожится. Хотелось бы чтобы диалог не уничтожался, а выдавалось сообщение QMessageBox.
Очевидно, что для этого нужно установить try {} catch(...) внутри метода exec() где-то в теле цикла обработки сообщений.
Кто-нибудь задавался таким вопросом?



Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 17, 2012, 07:30
Зачем использовать исключения, если проще написать функцию проверки валидности?
И к примеру подсвечивать незаполненные важные поля, при преждевременном нажатии ОК?

PS исключения хорошо, но помоему не в этом случае.


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 17, 2012, 08:01
Просто когда разрабатываешь диалог, слишком много получается кода, отвечающего за всякие проверки. И этот код очень трудно так структурировать, чтобы было все красиво и не было нагромождений.
Исключения, мне кажется, как раз бы уменьшили количество такого кода. К тому же обработчик находится в одном месте и он всего один.

Может у вас есть другой подход и вы знаете как избавиться от громоздко кода проверок?


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 17, 2012, 08:09
Кхм... громоздкого? Ну незнаю.

Помоему, в любом случае этот код у вас будет. И проверяться на валидность будет одними и теми же методами. (вру, как минимум 3 способами я себе представляю)

И никуда этот код не денется, разницей будет только реакция - либо посылка исключения/неактивная кнопка "ОК"/мессага о невалидности введённой строки.

PS или я не вижу более очевидных методов проверки?


Название: Re: C++ Exceptions и Qt
Отправлено: alexis031182 от Апрель 17, 2012, 08:12
Посмотрите QValidator. Как вариант.


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 17, 2012, 08:28
Ну хорошо. А как вы обрабатываете ошибку возникшую в конструкторе диалога?
Хотя это другой случай.


Название: Re: C++ Exceptions и Qt
Отправлено: Akon от Апрель 17, 2012, 08:33
Цитировать
Просто когда разрабатываешь диалог, слишком много получается кода, отвечающего за всякие проверки. И этот код очень трудно так структурировать, чтобы было все красиво и не было нагромождений.
Исключения, мне кажется, как раз бы уменьшили количество такого кода. К тому же обработчик находится в одном месте и он всего один.

Не размещайте логику в интерфейсе! Представьте, что наряду с гуем есть параллельно, скажем, консольный интерфейс - сразу увидите дублирующийся код проверок.

В VCL удобная система в плане обработки ошибок через исключения - требует минимум усилий. Чтобы достичь такого же комфорта в Qt нужно переопределять QCoreApplication::notify() и патчить сорцы QtCore (QObjectPrivate::activate() если не ошибаюсь). Последнее особенно непрактично.


Цитировать
К тому же обработчик находится в одном месте и он всего один.
Можно использовать такой шаблон:
Код:
void MyDialog::OnOkButtonClick()
{
    QWidget* propertyControl = lineEdit1;
    try {
        businessLogic.setProperty1(lineEdit1->text());  // throws at error

        propertyControl = lineEdit2;
        businessLogic.setProperty2(lineEdit2->text());  // throws at error
        ....
    }
    catch (const BusinessLogicError& e) {
        QMessageBox::warning(... e.what() ...);
        propertyControl->setFocus();
    }
}

Иногда уставонка значений/проверки используются при потере фокуса элементом - это другой шаблон.


Название: Re: C++ Exceptions и Qt
Отправлено: alexis031182 от Апрель 17, 2012, 08:52
Ну хорошо. А как вы обрабатываете ошибку возникшую в конструкторе диалога?
Хотя это другой случай.
В конструкторах должна быть лишь инициализация параметров класса значениями по умолчанию и/или на основе входных аргументов. Если требуется валидация входных аргументов, то лучше производить их внесение в класс в отдельной функции, типа

bool setParam(аргумент/список_аргументов);


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 17, 2012, 09:04
Однако, такой подход неудобен тем, что при первом исключении сразу уничтожается объект диалога.
Так можно и не уничтожать
Код
C++ (Qt)
MyDialog dlg(this);
while (true)
try
{
  dlg.exec();
  break;
}
catch(const std::exception& e)
{
// Вывод сообщения
}
}
 
Просто когда разрабатываешь диалог, слишком много получается кода, отвечающего за всякие проверки. И этот код очень трудно так структурировать, чтобы было все красиво и не было нагромождений.
Исключения, мне кажется, как раз бы уменьшили количество такого кода. К тому же обработчик находится в одном месте и он всего один.
Теория говорит что исключения не должны рассматриваться как альтернативный обработчик ошибок, а генерироваться только в том случае когда ситуация безвыходная, возможности обработки нет. Не очень верится что "ну очень трудно структурировать" в коде/методах диалога. Попытка сачкануть  :)


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 17, 2012, 09:22
Помоему это плохой тон - выводить исключения только при проверке параметров.

В VLC (могу и ошибаться) через исключения выводятся ошибки, приводящие в дальнейшем к крушению программы. К примеру кодека нету, но есть возможность (50 на 50), что запуститься и без него. ;)


Название: Re: C++ Exceptions и Qt
Отправлено: Akon от Апрель 17, 2012, 10:20
Цитировать
Теория говорит что исключения не должны рассматриваться как альтернативный обработчик ошибок, а генерироваться только в том случае когда ситуация безвыходная, возможности обработки нет.
Ой-ли, что за теория  :)

Цитировать
Помоему это плохой тон - выводить исключения только при проверке параметров.

В VLC (могу и ошибаться) через исключения выводятся ошибки, приводящие в дальнейшем к крушению программы. К примеру кодека нету, но есть возможность (50 на 50), что запуститься и без него.
А если параметр проверяется в глубоко вложенной функции - че будете переть код возврата через все уровни? И на счет VCL вы глубоко ошибаетесь. Там даже есть специальное исключение EAbort - типа просто отмена и все.

На самом деле исключения в широком смысле дают альтернативное ветвление программы для ваших нужд (в более узком смысле - это обработка ошибок), которое упрощает программирование за счет четкого разделения основной и альтернативной логик. Если в определенном смысле привести аналогию - это как std::cout и std::cerr в плане разделения вывода. Вы, конечно, можете ошибки херачить в std::cout и долго их потом искать. Смешение основной логики и логики обработки ошибок снижает читабельность и даже быстродействие кода.


Название: Re: C++ Exceptions и Qt
Отправлено: Авварон от Апрель 17, 2012, 10:28
Цитировать
Теория говорит что исключения не должны рассматриваться как альтернативный обработчик ошибок, а генерироваться только в том случае когда ситуация безвыходная, возможности обработки нет.
Ой-ли, что за теория  :)

Цитировать
Помоему это плохой тон - выводить исключения только при проверке параметров.

В VLC (могу и ошибаться) через исключения выводятся ошибки, приводящие в дальнейшем к крушению программы. К примеру кодека нету, но есть возможность (50 на 50), что запуститься и без него.
А если параметр проверяется в глубоко вложенной функции - че будете переть код возврата через все уровни? И на счет VCL вы глубоко ошибаетесь. Там даже есть специальное исключение EAbort - типа просто отмена и все.

На самом деле исключения в широком смысле дают альтернативное ветвление программы для ваших нужд (в более узком смысле - это обработка ошибок), которое упрощает программирование за счет четкого разделения основной и альтернативной логик. Если в определенном смысле привести аналогию - это как std::cout и std::cerr в плане разделения вывода. Вы, конечно, можете ошибки херачить в std::cout и долго их потом искать. Смешение основной логики и логики обработки ошибок снижает читабельность и даже быстродействие кода.
Тешьте себя, снижает, ага. А исключения не снижают.


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 17, 2012, 10:40
К сожалению, Akon, использование исключений в таком качестве, не рекомендуется всеми более-менее значимыми величинами в ООП.

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


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 17, 2012, 11:03
Ой-ли, что за теория  :)
Библия Стаутструпа
А если параметр проверяется в глубоко вложенной функции - че будете переть код возврата через все уровни?
Иногда приходится и переть. Конечно было бы здорово поставить где-то вверху catch чтобы сообщить "ах, извините, недостаточно памяти". Только не всегда удается так легко отделаться.

Проверка параметров никак не выглядит требующей исключений. Весь контекст/данные на руках, ну повыскакивал через return false - и все. Вероятно трудности с выводом сообщения об ошибке, точнее с его формированием. Это часто как бы "налипает" на каждую if проверку. Впрочем надо послушать автора темы, а то предположений можно сделать много.


Название: Re: C++ Exceptions и Qt
Отправлено: Akon от Апрель 17, 2012, 11:22
Цитировать
Тешьте себя, снижает, ага. А исключения не снижают.
Я ограничусь одним нетехническим аргументом: современные мейнстрим языки - какова роль исключений там? (Преобладающая).

Цитировать
К сожалению, Akon, использование исключений в таком качестве, не рекомендуется всеми более-менее значимыми величинами в ООП.

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

Цитировать
Библия Стаутструпа
Да ну? Насколько я помню, он там даже множественное наследование исключений пользует!

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


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 17, 2012, 12:00
Полагаю, вы хотели сказать, что не все ситуации, ошибочные на первый взгляд, следует считать таковыми и, соответственно, использовать под них исключения.
Я хотел сказать простой вещь
Цитировать
     Механизм особых ситуаций является менее структурированным,
 чем такие локальные структуры управления как операторы if или for.
 Обычно он к тому же является не столь эффективным, если особая
 ситуация действительно возникла. Поэтому особые ситуации следует
 использовать только в том случае, когда нет хорошего решения с более
 традиционными управляющими структурами, или оно, вообще, невозможно.
Пока в этой теме - просто (нормальный) интерес "а не будет ли с исключениями лучше/проще". Ну пока не видно ни одного основания использовать исключения. Сделать конечно можно, но выгод это не даст


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 17, 2012, 12:29
На самом деле мне близка позиция, высказанная товарищем Akon: обработка ошибок отделяется от основной логики программы. Я всегда использую такой подход в проектах С++. Где-то на самом верху стоит catch(), который ловит критические ошибки и выводит сообщения о типе ошибке, классе, методе, файле и строке, где произошла ошибка.
На локальном уровне генерируются и свои исключения, которые обрабатываются уровнем выше. Но обработка критических ошибок происходит все равно в одном месте - на самом верху. Исключение, брошенное где-то очень глубоко, начинает раскручивать стек и управление передается наверх в главный catch().

Таким же образом я хотел использовать и диалоги Qt. Некорректные действия пользователя в диалоге должны бросать исключение, которое должно обработаться внутри метода exec() - в теле цикла обработки событий. Обработчик  исключения должен вызывать скажем QMessageBox, а когда он будет закрыт, цикл обработки событий переходит к следующей итерации и диалог продолжает работать.


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 17, 2012, 12:37
Мде... Мне одному кажется, что некоторые действия в диалоге, которые вызывают исключение, которое раскручивает стек и уходит на самый верх, показывает месседж бокс и далее опять идёт вниз - черезчур черезпопно?


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 17, 2012, 12:50
Верес, стек раскручивается в данном случае не до самого верха, а до места вызова exec(). То есть обработчик ошибок диалога находится в месте вызова этого диалога.


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 17, 2012, 13:15
Угу. Очень интересно конечно, но помоему очень и очень излишне.
Всёравно эта проверка будет производиться в диалоге. Там же будет посылаться исключение. Так что мешает на месте распальцевать польлзователю, в чём он неправ???

Без всяких "условных" проверок, без раскручивания стека, без описания обработчика ошибок класса (диалога), который вынесен за пределы класса...

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


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 17, 2012, 13:49
Таким же образом я хотел использовать и диалоги Qt. Некорректные действия пользователя в диалоге должны бросать исключение, которое должно обработаться внутри метода exec() - в теле цикла обработки событий. Обработчик  исключения должен вызывать скажем QMessageBox, а когда он будет закрыт, цикл обработки событий переходит к следующей итерации и диалог продолжает работать.
Не видно удобного способа заключить обработчик в try. Также с событийной схемой/сигналами возможна масса заморочек - ведь то что уже попало в очередь может не будет убито исключением, или наоборот

обработка ошибок отделяется от основной логики программы. Я всегда использую такой подход в проектах С++. Где-то на самом верху стоит catch(), который ловит критические ошибки и выводит сообщения о типе ошибке, классе, методе, файле и строке, где произошла ошибка.
Конечно это звучит гордо - ну действительно, давайте отделим обработку ошибок! :) Но если взглянуть непредвзято - а так ли уж это хорошо? Печатать файл/строку легко и приятно, а вот сообщения для пользователя - другой табак. Выскочив наверх мы потеряли "контекст", по-простому говоря: многое что нам надо для показа в MessageBox у нас в локальных переменных и/или др классах. И теперь придется переть это все наверх, обычно раздувая классы исключений


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 17, 2012, 14:28
Наверх всё это не вынести. И потому получается рекурсия:

Вызов диалога -> ошибка -> ексцепшен -> выход наверх -> вызов данных из диалога -> ексцепшен -> выход наверх -> вызов данных из диалога -> -//- -//-

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

Задумайтесь людиии, вы пытаетесь болезнь отделить от больного. Вместо лечения больного.

оффтоп:
П - пациент, Д - доктор, ГВ - главврач
П - Доктор, у меня рак лёгких!
Д - Сейчас вызовем главного врача, он у нас хирург, всё будет хорошо. *делает звонок*
*проходит 15 минут, приходит главврач*
ГВ- Вырежем у вас лёгкие, заодно печень, почки и глаз. Проверим, поставим диагноз и вылечим.
*через день на могилке*
ГВ- Мы бы вылечили его, но нам нехватило кишечника, а он уже скончался...
Д - В следующий раз кишечник сразу возьмём ;)


Название: Re: C++ Exceptions и Qt
Отправлено: Akon от Апрель 17, 2012, 16:09
Верес:
Цитировать
Угу. Очень интересно конечно, но помоему очень и очень излишне.
Всёравно эта проверка будет производиться в диалоге. Там же будет посылаться исключение. Так что мешает на месте распальцевать польлзователю, в чём он неправ???

Без всяких "условных" проверок, без раскручивания стека, без описания обработчика ошибок класса (диалога), который вынесен за пределы класса...

Или я не понимаю всей серьёзности ваших диалогов. Там что, память пользователь мышкой проведя по лайнэдиту распиливает???
Вы не понимаете не серьезности диалогов, а серьезности подхода  :) В диалоге вы выбрасываете исключение со всей необходимой информацией, а "распальцовку" делает не диалог, а код, находящийся выше по стеку. Мне лень в каждом диалоге или окне каждого проекта писать стереотипный код с MessageBox-ом, за меня это сделает обработчик исключения уровнем повыше.

Igors:
Цитировать
Выскочив наверх мы потеряли "контекст", по-простому говоря: многое что нам надо для показа в MessageBox у нас в локальных переменных и/или др классах. И теперь придется переть это все наверх, обычно раздувая классы исключений
Контекст, как вы заметили, сохраняется в исключении. А на самом верху используется всего то exception.what(). Раздутый класс исключения - это о чем?

Цитировать
Не видно удобного способа заключить обработчик в try.
QCoreApplication::notify()

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

silart:
В Qt нет такой удобной реализации, как в VCL. Как я уже говорил, чтобы ее заиметь нужно:
1. Поработать над QCoreApplication::notify(), т.е. установить обработчик верхнего уровня для всех событий.
2. Изменить код вызова слотов в QtCore и перекомпилировать либу (непрактично).

Код, который вы привели в первом посте
Код:
try
{
   MyDialog dlg(this);
   dlg.exec();
}
catch(const std::exception& e)
{
// Вывод сообщения
}
это не то, как, вы сами же и заметили
Цитировать
Очевидно, что для этого нужно установить try {} catch(...) внутри метода exec() где-то в теле цикла обработки сообщений.


Название: Re: C++ Exceptions и Qt
Отправлено: Akon от Апрель 17, 2012, 16:19
Вот пример:

// MainWindow.h
Код:
#include <QtGui/QDialog>
#include <stdexcept>

// Dialog

class Dialog : public QDialog
{
Q_OBJECT

public:
Dialog()
{
QMetaObject::invokeMethod(this, "handler", Qt::QueuedConnection);
}

private:
Q_SLOT void handler()
{
throw std::runtime_error("Error message for user");
}
};

// Main.cpp
Код:
#include "MainWindow.h"
#include <QtGui/QApplication>
#include <QtGui/QMessageBox>
#include <stdexcept>

// Application

class Application : public QApplication
{
public:
Application(int argc, char *argv[]) : QApplication(argc, argv)
{
}

protected:
virtual bool notify(QObject * o, QEvent * e)
{
try {
return QApplication::notify(o, e);
}
catch (const std::exception& e) {
QMessageBox::critical(0, QApplication::instance()->applicationName(),
e.what(), QMessageBox::Ok, QMessageBox::NoButton);
}
catch (...) {
QMessageBox::critical(0, QApplication::instance()->applicationName(),
"Unexpected application error", QMessageBox::Ok, QMessageBox::NoButton);
}

return false;
}
};

// Entry

int main(int argc, char *argv[])
{
Application app(argc, argv);
Dialog dialog;
dialog.exec();  // исключение выбрасывается здесь, здесь же оно и обрабатывается
return 0;
}


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 17, 2012, 16:58
В диалоге вы выбрасываете исключение со всей необходимой информацией, а "распальцовку" делает не диалог, а код, находящийся выше по стеку. Мне лень в каждом диалоге или окне каждого проекта писать стереотипный код с MessageBox-ом, за меня это сделает обработчик исключения уровнем повыше.
Сравнивая 2 варианта
Код
C++ (Qt)
if (!Validate()) throw MyException(..);
или
if (!Validate()) return ShowError(..);
 
я не вижу никаких преимуществ первого. Что же "распальцевало" исключение? Да по сути ничего, показало месягу с тем что дали (e.what), с тем же успехом могу сделать это просто ф-цией/методом. А львиная доля работы с ошибками часто именно в подготовке этого what. С этой точки зрения вынесение обработчика наверх ничего не дает


Название: Re: C++ Exceptions и Qt
Отправлено: alexis031182 от Апрель 17, 2012, 17:03
Меня интересует вопрос: а что получится, если диалог, окно, да просто виджет/объект (QObject) будет создан через new, да при том без указателя на родителя (this)? Как узнать в обработчике исключения, что объект надо удалить, если он выбивается из ряда других себе подобных и создан динамически?

В этой теме, как я понял, исключения рассматриваются не с точки зрения критических ошибок, а просто как средство замены нативных системы событий и сигнал/слотов, правильно?


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 17, 2012, 19:51
Угу. Люди поняли - вот он стереотип счастья. Вместо написания 1 (одной) проверки, можно отослать наверх исключение с багажником необходимой информации. И всё ради 1 месседж бокса.

Боженька, прошу тебя  никогда мне не посылай такого чужого кода!!!



Название: Re: C++ Exceptions и Qt
Отправлено: V1KT0P от Апрель 18, 2012, 06:49
Угу. Люди поняли - вот он стереотип счастья. Вместо написания 1 (одной) проверки, можно отослать наверх исключение с багажником необходимой информации. И всё ради 1 месседж бокса.

Боженька, прошу тебя  никогда мне не посылай такого чужого кода!!!
Я полностью с тобой согласен. Меня особо бесит IBPP, там исключения попихали ку-да только можно. И получается простыня из try catch на несколько экранов. Попробовал обойтись без обертки, не так удобно зато никакой каши из-за исключений, да и гибкость повысилась. Если мне например на неудачный запрос в базу надо откатить до предыдущего состояния и повторить без него, то без исключений там элементарно. С исключениями же либо использовать их 100500 штук да еще и с переменными, которые вернут последнее состояние из исключений, получается такая жопа.
Я считаю что исключения должны использоваться исходя из их названия - в исключительных случаях которые по каким-то причинам не предусмотрел программист и дальнейшая нормальная работа не возможна.


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 18, 2012, 06:54
Мне вот исключения представляются просто - критическое оповещение, когда программа уже летит в тартарары.

И выдавать оно должно только критичную информацию. Типа "чувак у тебя тут 20-й элемент в 5-элементном массиве запрашивается".

И уж точно - никогда не обрадуешься зависимости исключений в вышестоящих классах и нижестоящих ;)


Название: Re: C++ Exceptions и Qt
Отправлено: Akon от Апрель 18, 2012, 07:49
Цитировать
Сравнивая 2 варианта
Код
C++ (Qt)
if (!Validate()) throw MyException(..);
или
if (!Validate()) return ShowError(..);
 
я не вижу никаких преимуществ первого. Что же "распальцевало" исключение? Да по сути ничего, показало месягу с тем что дали (e.what), с тем же успехом могу сделать это просто ф-цией/методом. А львиная доля работы с ошибками часто именно в подготовке этого what. С этой точки зрения вынесение обработчика наверх ничего не дает
1. Хоть сколько нибудь серьезная программа никогда не будет размещать бизнес логику (Validate) в слое GUI.
2. В слое бизнес логики вы не можете использовать if (!Validate()) return ShowError(..); поскольку ShowError - это конкретное действие, допустим MessageBox. А если консольная прога? Там не нужен MessageBox. Получается, что бизнес логика зависит от типа приложения. Да, можно абстрагироваться от конкретного вывода ошибки, но получается дополнительная зависимость бизнес логики, которой нет в случае исключений.
3. Нет механизма структурированной обработки ошибки (в нетривиальной бизнес логике это важнейший аспект).

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

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


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 18, 2012, 08:33
Akon - я тебе ещё про один альтернативный вид ветвления расскажу - goto. Попробуй, тебе понравится!!!

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

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

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

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

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

Кхм. Опять таки вы мешаете валидацию (проверку на правильность параметров) и ошибки. Походу это болезнь...


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 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, пусть наверху разбираются (поправьте если не так). Это дело хорошее, но какая в этом необходимость для диалоге ввода? Очень может быть что и никакой.

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


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 18, 2012, 10:43
Спасибо всем за высказанные мнения!  ;)

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

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


Название: Re: C++ Exceptions и Qt
Отправлено: navrocky от Апрель 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
}


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 18, 2012, 12:57
Мда... Исключения, которые ловятся не всегда и не везде... Использовать их везде...

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


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 18, 2012, 13:35
Исключения - это хорошо. Я их у себя везде использую, ..
Вот это честный ответ. Используется по той простой причине что уже использовалось - и результаты были хороши. Так почему бы не использовать это и в др проекте, ведь многие места уже известно как делать.

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

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

Подумайте что Вам понадобится для такой переделки - и Вы найдете очень много интересного. Это за спиной мощного инструментария легко калякать об "архитектуре" - а дойдет до дела - так "мама не горюй"  :)


Название: Re: C++ Exceptions и Qt
Отправлено: navrocky от Апрель 18, 2012, 14:25
Мда... Исключения, которые ловятся не всегда и не везде... Использовать их везде...

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

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

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

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

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

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

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


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 18, 2012, 14:42
Например, открыли файл - проверили, написали код для обработки ошибки, затем прочли N байт - проверили что прочли N байт, написали код для случая, когда не прочли. и т.д. В итоге получается бООльшая простыня из кучи проверок.
Ну а диалог ввода-то здесь причем? :) Приведя один пример Вы чего-то обобщаете его на все случаи жизни. Хотя не спорю, пример удачный, здесь "проверять после каждого" нереально.



Название: Re: C++ Exceptions и Qt
Отправлено: Akon от Апрель 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. Хотелось бы взглянуть на такой подход на практике. Не могли бы вы показать код небольшого класса, где используется такой подход

Это настолько фундаментальная концепция, что даже не знаю :D
Код:
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());
    }
};


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 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 хотя намерения благие, но средства достижения избыточны. ИМХО ;)


Название: Re: C++ Exceptions и Qt
Отправлено: V1KT0P от Апрель 18, 2012, 16:16
Ну да, при этом надо скурпулезно проверять возвращаемое значение каждого метода. Иначе программа будет просто молча не работать.

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

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


Название: Re: C++ Exceptions и Qt
Отправлено: navrocky от Апрель 18, 2012, 16:27
Ну дык и ловить исключения тоже можно забыть или одно какое-то исключение пропустить и тоже ничего хорошего не будет. Я себе плохо представляю как можно на исключениях строить логику, это-же получится простыня из catch-ей. Выделили память в куче в функции и вызвали другую функцию, она бросила исключения стек свернулся а память в куче так занята и осталась. Исключение это goto, но только не скованный пределом функции.

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


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 18, 2012, 16:28
Цитировать
Чем вот это, отличается от:
Код:
      
if (value % 1)
    emit error();

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

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

Верес, emit отличается от throw тем, что код после throw, в случае его срабатывания, не исполняется. Когда бросаем throw, начинается раскрутка стека, в результате которой вызываются деструкторы всех созданных в стеке объектов и утечка ресурсов не происходит.
Когда же вы вызываете emit, это приводит к вызову всех слотов, и после этого код стоящий после emit будет выполняться далее.


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 18, 2012, 16:31
navrocky, в вашем случае, когда у вас перестали ловиться исключения в notify(), разве нельзя было пересобрать Qt?
Вы используете shared или static сборку Qt?


Название: Re: C++ Exceptions и Qt
Отправлено: navrocky от Апрель 18, 2012, 16:37
navrocky, в вашем случае, когда у вас перестали ловиться исключения в notify(), разве нельзя было пересобрать Qt?
Вы используете shared или static сборку Qt?

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

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


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 18, 2012, 16:37
Верес, emit отличается от throw тем, что код после throw, в случае его срабатывания, не исполняется. Когда бросаем throw, начинается раскрутка стека, в результате которой вызываются деструкторы всех созданных в стеке объектов и утечка ресурсов не происходит.
Когда же вы вызываете emit, это приводит к вызову всех слотов, и после этого код стоящий после emit будет выполняться далее.
Ну а почему дальнейшее выполнение "обязательно плохо"? Это зависит от задачи, напр в примере с диалогом ввода я лично ничего плохого не вижу.

Ну если уж попали на любителей исключений, то такой вопрос: а что будет если при пресловутой "раскрутке стека" произойдет еще одно исключение?


Название: Re: C++ Exceptions и Qt
Отправлено: navrocky от Апрель 18, 2012, 16:44
Ну если уж попали на любителей исключений, то такой вопрос: а что будет если при пресловутой "раскрутке стека" произойдет еще одно исключение?

Деструкторы не должны кидать исключения, если кидают - это надо исправлять.


Название: Re: C++ Exceptions и Qt
Отправлено: V1KT0P от Апрель 18, 2012, 16:45
Ну дык и ловить исключения тоже можно забыть или одно какое-то исключение пропустить и тоже ничего хорошего не будет. Я себе плохо представляю как можно на исключениях строить логику, это-же получится простыня из catch-ей. Выделили память в куче в функции и вызвали другую функцию, она бросила исключения стек свернулся а память в куче так занята и осталась. Исключение это goto, но только не скованный пределом функции.

Все оттого, что реально их не использовали. У меня один try/catch на самом верху в notify, который рапортует об ошибке (хотя теперь для надежности еще оборачиваю слоты). В остальном я о них не думаю, просто пишу код. От утечек спасают смарт-поинтеры, кутэшное владение QObject и RAII. Стараюсь вообще обычные указатели не использовать. Исключение - хитрые оптимизации, но такого кода у меня очень мало.
Да сам я логику никогда на исключениях не строил. Но вот библиотекой IBPP которая таки напичкана исключениями до сих пор пользуюсь так вот обычная функция которая всего-то должна выполнить запросы превращается в кашу из try catch. Когда же потребовалось чуть больше гибкости я выбросил библиотеку и использовал АПИ напрямую, так код получился просто шикарным. Я не за то чтоб полностью отказаться от исключений. Меня бесит только чрезмерное их употребление, особенно там где без этого можно обойтись.
Например надо записать данные в файл. Перед записью проверяется наличие файла и тут вопрос: На кой кидать исключение которое вызовет функцию создания файла а затем выполнить функцию записи? Не проще ли сперва в самой функции вызвать функцию создания файла и уж если она не сработает и больше сделать нечего то да тут уже надо бросать исключение ибо выхода нет. Но вот как ветвление использовать исключение это слишком. Это ты своем коде легко разбираешься, а вот если тебе дадут большой проект где ветвления будут через исключения, посмотрим как ты будешь там ориентироваться.


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 18, 2012, 16:59
А я без notify() обрабатывал исключения следующим образом:

Код:
	QApplication a(argc, argv);
PlatformManager pmgr(NULL, Qt::Window);

try
{
pmgr.show();
a.exec();
}
catch(ml::Exception& e)
{
ml::qt::ErrorBoxDialog::show("Калибратор", e.title().c_str(), e.what().c_str(), &pmgr);

return 0;
}

navrocky , что вы думаете по этому поводу?


Название: Re: C++ Exceptions и Qt
Отправлено: V1KT0P от Апрель 18, 2012, 17:02
navrocky , что вы думаете по этому поводу?
Ну это совсем не то про что идет речь. Речь идет про использование исключений для ветвлений логики, у вас же просто информирование про ошибку и закрытие программы.


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 18, 2012, 17:06
Просто шла речь про то, что notify() недокументирован на предмет использования исключений.


Название: Re: C++ Exceptions и Qt
Отправлено: navrocky от Апрель 18, 2012, 18:25
Перед записью проверяется наличие файла и тут вопрос: На кой кидать исключение которое вызовет функцию создания файла а затем выполнить функцию записи? Не проще ли сперва в самой функции вызвать функцию создания файла и уж если она не сработает и больше сделать нечего то да тут уже надо бросать исключение ибо выхода нет.
Да, за такое использование исключений надо по рукам бить. Надо проверить что файл есть, если нет то создать, если не создается - кинуть исключение.


Название: Re: C++ Exceptions и Qt
Отправлено: navrocky от Апрель 18, 2012, 19:07
А я без notify() обрабатывал исключения следующим образом:

Код:
	QApplication a(argc, argv);
PlatformManager pmgr(NULL, Qt::Window);

try
{
pmgr.show();
a.exec();
}
catch(ml::Exception& e)
{
ml::qt::ErrorBoxDialog::show("Калибратор", e.title().c_str(), e.what().c_str(), &pmgr);

return 0;
}

navrocky , что вы думаете по этому поводу?

Ну как вариант сойдет. Но помните что шансы словить исключение этим catche'м не стопроцентные. Т.к. исключение будет проходить через кутэшную прослойку, что может привести к различным эффектам.
У меня, например, был случай когда так исключения не ловились и программа завершалась по abort() как это происходит, когда кидаешь исключение и не ловишь его. С 3-м кутэ и с 4-ым.


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 18, 2012, 21:04
Угу. Куте с радостью пропускает некоторые исключения и вызывает свою (умную) реакцию.  ;)

Мне просто интересно, что может испортить выполнение дальнейшего кода?

Например в данном случае(нажатие на ок)
Код:
if (ui.spinBox->value() < 20) // вот это ошибочная проверочка
{
emit error(); // или окно с ошибочкой
return;
}
int x[20];
x[ui.spinBox->value()] = 0;


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 19, 2012, 05:15
Ну как вариант сойдет. Но помните что шансы словить исключение этим catche'м не стопроцентные. Т.к. исключение будет проходить через кутэшную прослойку, что может привести к различным эффектам.
У меня, например, был случай когда так исключения не ловились и программа завершалась по abort() как это происходит, когда кидаешь исключение и не ловишь его. С 3-м кутэ и с 4-ым.

Может быть это связано с тем, что Qt может быть собрана с ключем no-exceptions, и в этом случае какой-нибудь макрос везде устанавливает catch(...) {}. Вот исключения и не проходят. C++ то никто еще не отменял!  ;)
Но в моем случае это не страшно, потому что я сам собираю Qt.


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 19, 2012, 05:30
Мне просто интересно, что может испортить выполнение дальнейшего кода?

Например в данном случае(нажатие на ок)
Код:
if (ui.spinBox->value() < 20) // вот это ошибочная проверочка
{
emit error(); // или окно с ошибочкой
return;
}
int x[20];
x[ui.spinBox->value()] = 0;

Верес, ваш код
Код:
emit error();
return;

отличается от кода
Код:
throw std::logic_error("Error");
тем, что после вызова слотов вы ставите return. То есть вы пытаетесь имитировать механизм исключений. В таком виде ваш код неявно вызовет только деструкторы объектов находящиеся в контексте вашего слота. Если ваш слот будет вызван из другой функции, а эта функция в свою очередь из другой, а если у этих функций есть стековые объекты в своих контекстах, их деструкторы вызваны не будут и произойдет утечка ресурсов.

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

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


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 19, 2012, 07:03
Кхм. То есть у вас, если диалог выдаст ошибку валидации, вы вызовете деструкторы диалога, деструкторы его родителя виджета и прочего, вплоть до всех созданных объектов?

Помоему у вас с логикой не то.

Моя конструкция с return НЕ ДОПУСКАЕТ возникновения исключительной ситуации ;) Как бы ты не изощрялся, проверку пробить может только порча памяти в моей же программе, а от этого не спасёт даже твоё исключение ;)

А благодаря логической структуре программы, нет необходимости в "убиении" всех свидетелей этого "позора".

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

Да и действительно, приведите пожалуйста свой код "среднего" try/catch в ваших программах. Хочется оценить величину простыни и понятность кода.


Название: Re: C++ Exceptions и Qt
Отправлено: Akon от Апрель 19, 2012, 07:26
Цитировать
Да и есть один аргумент. Если Qt пишется без учета исключений, а прослойка в обработчике евентов там довольно жирная - значит есть довольно большой шанс, что как минимум могут ресурсы утечь, что-то недоинициализируется... Т.к. это недокументированная возможность ловли исключений через QCoreApplication::notify, не исключено, что если сегодня оно не сегфолтится и не течет, то завтра вполне это может случиться.
Да, жирная прослойка меня тоже очень насторожила, но вот код из QEventLoop::exec():
Код:
int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (d->threadData->quitNow)
        return -1;

    if (d->inExec) {
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);
        return -1;
    }
    d->inExec = true;
    d->exit = false;
    ++d->threadData->loopLevel;
    d->threadData->eventLoops.push(this);

    // remove posted quit events when entering a new event loop
    QCoreApplication *app = QCoreApplication::instance();
    if (app && app->thread() == thread())
        QCoreApplication::removePostedEvents(app, QEvent::Quit);

#if defined(QT_NO_EXCEPTIONS)
    while (!d->exit)
        processEvents(flags | WaitForMoreEvents | EventLoopExec);
#else
    try {
        while (!d->exit)
            processEvents(flags | WaitForMoreEvents | EventLoopExec);
    } catch (...) {
        qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
                 "exceptions from an event handler is not supported in Qt. You must\n"
                 "reimplement QApplication::notify() and catch all exceptions there.\n");

        // copied from below
        QEventLoop *eventLoop = d->threadData->eventLoops.pop();
        Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
        Q_UNUSED(eventLoop); // --release warning
        d->inExec = false;
        --d->threadData->loopLevel;

        throw;
    }
#endif

    // copied above
    QEventLoop *eventLoop = d->threadData->eventLoops.pop();
    Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
    Q_UNUSED(eventLoop); // --release warning
    d->inExec = false;
    --d->threadData->loopLevel;

    return d->returnCode;
}
Как видно, в #else ветке условия #if defined(QT_NO_EXCEPTIONS) мы имеем варнинг "Qt has caught ...", по которому можно судить, что возможность использования исключений есть, и прослойка Qt будет exception safe. Хотя, я реально exception safety не проверял.


Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 19, 2012, 13:03
Выброс исключения в контексте какой-то внутренней функции вызывает раскрутку стека и все стековые объекты корректно удалятся, а логика работы программы пойдет уже по альтернативному пути, т. е. дальнейшие действия (стоящие за вызовом вашего сбойного слота) выполнены не будут. Благодаря этому вы избавитесь от ошибок, возникающих время от времени то тут то там.
Вот в чем смысл использования исключений.
Посмотрите что стоит в catch в исходнике exec которые привел Akon. Чудесная "раскрутка" почему-то не сделала (пере)установку inExec и loopLevel, и приложение рухнет если убрать это из catch. Также заметим что с пере-испусканием throw утеряна вся информация об исключении.

А тот "альтернативный путь" далеко не всегда возможен и/или желателен. Я не против исключений, но в меру, а то судя по Вашим словам - ну прямо "искусственный интеллект"  :)


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 19, 2012, 13:19
Нейросеть + ексепшены = скайнет.

То-то у него постоянно глючило всё. То робот не услышит, то летает неправильно, то ездит с неположенной скоростью ;)


Название: Re: C++ Exceptions и Qt
Отправлено: navrocky от Апрель 19, 2012, 14:39
Посмотрите что стоит в catch в исходнике exec которые привел Akon. Чудесная "раскрутка" почему-то не сделала (пере)установку inExec и loopLevel, и приложение рухнет если убрать это из catch.
Не вижу ничего подозрительного, все откатывается корректно в catch.

Цитировать
Также заметим что с пере-испусканием throw утеряна вся информация об исключении.
Ну что вы. Откуда такая информация?


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 19, 2012, 14:50
А да, интересный вопрос.

Если при раскрутке стека, произошла ещё парочка исключений, куда они денутся?

т.е. например:

валидация в диалог -> ексепшн "валид Еррор"
виджет главного окна -> ексепшн "выход за пределы массива"
менеджер окон -> ексепшн "порча памяти"
и наконец вываливается это всё к обработчику, но.... Но что придёт в конце? :P



Название: Re: C++ Exceptions и Qt
Отправлено: Igors от Апрель 19, 2012, 15:30
Не вижу ничего подозрительного, все откатывается корректно в catch.
В catch да, разговор о том что "раскрутка" сама по себе еще не гарантирует корректный откат

Цитировать
Также заметим что с пере-испусканием throw утеряна вся информация об исключении.
Ну что вы. Откуда такая информация?
Из приведенных исходников. Если хочу поставить обработчик выше exec, то смогу ловить только catch (...) + буду утыкан Qt сообщениями  :)


Название: Re: C++ Exceptions и Qt
Отправлено: iroln от Апрель 19, 2012, 16:01
Цитировать
1. Хоть сколько нибудь серьезная программа никогда не будет размещать бизнес логику (Validate) в слое GUI.
А логика и gui в Qt разбиты на ui класса и, собственно, сам класс. Почитайте что-ли книжки, а?
Так-то оно так, но как не выкорячивайся, всё равно в "собственно самом классе" вам всё равно придётся дёргать gui-объекты - это, как пример, поставить новый текст в лэйбл, опросить состояние комбобокса, зажигать/гасить виджеты и т.п. В итоге получается каша, как не крути. А пляска с setEnabled - это вообще застрелиться. Если вот это так, а это так, а то эдак, значит эту кнопку погасить, ту зажечь, а тут стало работать неправильно, забыли, что там ещё что-то влияет на текущее состояние и понеслась... всё это превращается в трудноразбираемую лапшу в геометрической прогрессии, в итоге всё выкидывается и пишется заново с навороченными менеджерами состояний, шаблоне на шаблоне на шаблоне и т.д. Ненавижу.  :-[

По поводу исключений, вон в питоне они везде используются и ничего, всё ок. Даже итераторы бросают исключение при завершении. try/finally и менеджеры управления контекстом позволяют не испытывать головную боль при использовании исключений. В С++ конечно с исключениями посложнее, поэтому многие от них отказываются. У нас в проектах на С++ запрещены исключения.


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 19, 2012, 17:57
Да и действительно, приведите пожалуйста свой код "среднего" try/catch в ваших программах. Хочется оценить величину простыни и понятность кода.

Привожу:

Сам класс исключений:
Код:
	class Exception : public boost::exception
{
public:
std::string title() const;
std::string message() const;
long code() const;
std::string function() const;
std::string file() const;
int line() const;

std::string what() const;
};

#define ML_ASSERT(exp) ((exp)? ((void)0): ml::AssertionFailed("Ошибка времени выполнения", #exp, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))

#define ML_THROW(msg) throw ml::Exception() \
<< ml::__title__("Ошибка времени выполнения") \
<< ml::__message__(msg) \
<< ml::__function__(BOOST_CURRENT_FUNCTION) \
<< ml::__file__(__FILE__) \
<< ml::__line__(__LINE__)

ML_THROW, ML_ASSERT - это макросы, упрощающие генерацию исключений.

Вот небольшой класс, который бросает исключения:
Код:
	class Reader : public boost::noncopyable
{
private:
bifstream m_fileStream; ///< Файловый поток
std::vector<Point> m_points; ///< Буфер прочитанных значений
std::string m_caption; ///< Название
unsigned int m_channels; ///< Число каналов
unsigned int m_frequency; ///< Частота

private:
void readHeader();
void readPoints();

public:
Reader(const boost::filesystem::path& filename);
std::string caption() const;
unsigned int channels() const;
unsigned int frequency() const;
size_t size() const;

std::vector<Point> read(size_t base, size_t num);
std::vector<double> read(size_t base, size_t num, unsigned int channel);

Point operator[](size_t index) const;
};

Часть реализации вышеприведенного класса:
Код:
std::vector<Point> Reader::read(size_t base, size_t num)
{
using namespace std;

vector<Point> result;

if (base >= 0 && base < m_points.size() && (base + num) <= m_points.size())
{
result.reserve( num );

copy(m_points.begin() + base,
m_points.begin() + base + num,
back_inserter(result));
}
else
{
boost::format msg("illegal read interval: \"[%1%; %2%]\"");
msg % base % (base + num);
ML_THROW(msg.str());
}

return result;
}

std::vector<double> Reader::read(size_t base, size_t num, unsigned int channel)
{
using namespace std;
using namespace boost::phoenix;

vector<double> result;

if (channel >= channels() )
{
boost::format msg("channel value \"%1%\" is not correct. Channels number: \"[%2%\"");
msg % channel % channels();
ML_THROW(msg.str());
}

if (base < 0 || base >= m_points.size() || (base + num) > m_points.size())
{
boost::format msg("illegal read interval: \"[%1%; %2%]\"");
msg % base % (base + num);
ML_THROW(msg.str());
}

result.reserve(num);

transform(m_points.begin() + base, m_points.begin() + base + num, back_inserter(result),
boost::phoenix::bind<double>(&Point::operator[], _1, channel));

return result;
}

Point Reader::operator[](size_t index) const
{
if (index > m_points.size() - 1)
{
boost::format msg("index value \"%1%\" out of range: \"[%2%; %3%]\"");
msg % index % 0 % (m_points.size() - 1);
ML_THROW(msg.str());
}

return m_points[index];
}
Обратите внимание как бросаются исключения.

А вот класс, который использует Reader и обрабатывает его исключения:
Код:
class PlayerWD : public InternalWD
{
friend class AbstractWD;

osc::ReadStream m_stream; ///< Поток для чтения
boost::shared_ptr<ml::thread> m_thread; ///< Внутренний поток
double m_frequency; ///< Частота следования измеренных значений из файла
Buffer m_buffer; ///< Буфер последних значений
bool fIsEof; ///< Флаг конца файла
bool fIsFailed; ///< Флаг ошибки

private:
void routine();

private:
PlayerWD(const std::string& filename);

public:
~PlayerWD();

AbstractWD::Sample Weight();
};

А вот как происходит обработка исключений, сгенерированных где-то в глубине класса Reader.
Код:
PlayerWD::PlayerWD(const std::string& filename) : 
m_buffer(SAMPLE_BUFFER_SIZE), m_frequency(0), fIsEof(false), fIsFailed(false)
{
using namespace boost;
using namespace boost::filesystem;

path p(filename);

shared_ptr<osc::Reader> reader(new osc::Reader( p ));
m_stream.setReader(reader);
m_frequency = reader->frequency();

if (m_frequency == 0)
ML_TITLE_THROW(MSG__PLAYERWD_ERROR, "Invalid file");

// Запускаем цикл выборки значений из файла
m_thread.reset( new ml::thread(boost::bind(&PlayerWD::routine, this)) );
m_thread->start();
}

void PlayerWD::routine()
{
try
{
Sample s;

m_stream >> s;

m_buffer.put( s );
}
catch(osc::eof&)
{
fIsEof = true;

// Помещаем в буфер пустой сэмпл
// Нужно чтобы выпустить клиентский поток,
// читающий в данный момент из буфера, если буфер пуст
m_buffer.put( Sample() );

m_thread->stop();
}
catch (ml::Exception& e)
{
// Проблема копирования считанного значения в селектор
fIsFailed = true;
Log::out() << e.what() << endl;

// Помещаем в буфер пустой сэмпл
// Нужно чтобы выпустить клиентский поток,
// читающий в данный момент из буфера, если буфер пуст
m_buffer.put( Sample() );

m_thread->stop();
}

// Выдавать измерения нужно с заданной частотой
sleep_us( int(1000 / m_frequency) );
}

Поясняю, класс PlayerWD в отдельном потоке циклически опрашивает класс ReadStream (это функция routine() ), который обращается к классу Reader.



Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 20, 2012, 06:58
И после этого вам не кажется такое количества кода излишним? :)

Тут и на 3 простыни хватит, если не на 5.


Название: Re: C++ Exceptions и Qt
Отправлено: silart от Апрель 20, 2012, 08:05
И после этого вам не кажется такое количества кода излишним? :)

Тут и на 3 простыни хватит, если не на 5.

Верес, важно не количество, а качество!  :)
Просто довольно сложный проект. Я вам хотел показать каким образом у меня происходит генерирование и обработка исключений.


Название: Re: C++ Exceptions и Qt
Отправлено: Bepec от Апрель 20, 2012, 08:12
Ну тут я увидел только вывод исключения, которое используется как сигнал куте.

И используется не в куте, а бусте. Зачем мешать сладкое с холодным???

Непоняяяяятно.

PS конечн интересно рассуждать о Qt и исключениях, думать при этом о программах на бусте, и в примерах приводить буст. Финита ля пародиа.