Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Qt & C++ :: Обработка ошибок и отладка 
:(
    Опции темы
Majestio
Дата 5.7.2013, 13:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 83
Регистрация: 19.12.2010

Репутация: нет
Всего: нет



Вобщем вопрос встал совсем уж конкретно. Суть в следующем:

В простейших программах написал проверку, вывел сообщение, если есть возможность обработать, обработал. Все понятно и прозрачно. Но когда программа вырастает в достаточно большой проект, когда стек вызова вырастает до 10-15 вложенных процедур (а при этом количество созданных экземпляров классов от 50), начинает съезжать крыша. Ну хорошо, программу худо бедно assert'ами и qDebug'ами отладил. Дальше начинается ввод в эксплуатацию, и там явно будут ошибки. Сообщение "Ошибка при работе с базой данных" не скажет ровным счетом ничего.

Далее опишу моменты, которые у меня сейчас встречаются, и которые я обрабатываю "как умею"

Вопрос 1. Поиск ошибок в коде

Использую __FILE__ и __LINE__ . Они указывают место в коде. Но есть "но". См "псевдо-код":

1) если (!процедура_удаления_старых_данных) вывести уведомление

2) процедура_удаления_старых_данных 
{
   2.1) открыть транзакцию
   2.2) заблокировать на update нужные записи
   2.3) удалить записи
   2.4) закоммитить транзакцию
   2.5) записать изменения в служебную таблицу-лог
}

Допустим на шаге 2.3 рвется связь с базой. Естественно БД самостоятельно откатывает транзакцию. Хорошо, мы отработали проверку и вывели ошибку - "ошибка удаления записей". Вопрос - в каком объекте, удаление каких данных, и пр. В идеале бы хотелось мы видеть весть стек вызова со всеми параметрами.

Пока решил примитивным способом - вывел все  операции с базой в класс с глобальной видимостью. В классе определил QString Error. Если метод класса отрабатывает с ошибкой, он в эту переменную пишет подробности. В коде использую макросы:

Код


#define GuiCheckContinue()  if (Global.Error!="")  { \
QMessageBox::warning(0, QObject::tr("Внимание!"), \
Global.Error + \
QObject::tr("<hr>Файл: \"")+ \
QFileInfo(__FILE__).fileName()+ \
QObject::tr("\"<BR>Строка: ")+ \
QString::number(__LINE__)); \
qCritical() << Global.Error + \
QObject::tr(" Файл: \"")+ \
QFileInfo(__FILE__).fileName()+ \
QObject::tr("\" Строка: ")+ \
QString::number(__LINE__);  \
Global.Error=""; \
}

#define GuiCheckReturn()  if (Global.Error!="")  { \
QMessageBox::warning(0, QObject::tr("Внимание!"), \
Global.Error + \
QObject::tr("<hr>Файл: \"")+ \
QFileInfo(__FILE__).fileName()+ \
QObject::tr("\"<BR>Строка: ")+ \
QString::number(__LINE__)); \
qCritical() << Global.Error + \
QObject::tr(" Файл: \"")+ \
QFileInfo(__FILE__).fileName()+ \
QObject::tr("\" Строка: ")+ \
QString::number(__LINE__);  \
Global.Error=""; \
return;\
}


В коде это выглядит примерно так:

Код

if (!Global.DataQuery.exec()) { 
  GuiSqlErrorData("Ошибка при работе с БД!"); 
  Global.RollbackTransaction(); 
  GuiCheckContinue(); 
  return; 
}
Global.CommitTransaction();                     GuiCheckReturn();
Global.DelLocker("Rubricator",Num);             GuiCheckReturn();
Global.SetOperation("Rubricator",Num,3,"");     GuiCheckReturn();
Global.Notify("Rubricator","*");                GuiCheckReturn();


Выводится в случае ошибки нечто, типа:

user posted image

Вобщем меня это как-то не устраивает. Какие есть мысли?

Вопрос 2. Обработка отката занятых ресурсов в случае ошибки

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

Если есть желание, подумайте над "простеньким" вопросом. Вот типичный алгоритм:

1) Открыли транзакцию
2) Заблокировали запись
3) Занесли информацию с служебную таблицу - кто заблокировал и на какую операцию
4) Отредактировали запись
5) Закоммитили транзакцию
6) Удалили инфу о вашей блокировке
7) Записали в журнал операций что вы сделали с записью

На шагах 1-2-4-5 используется первое соединение с базой, с 3-6-7 второе (назовем служебное). Чтобы "служебные" операции не попадали в транзакционный блок. Теперь представим случай, на шаге 5 связь с СУБД рвется, коммит не прошел. Что делать дальше?

Вощем буду рад любым идеям

Это сообщение отредактировал(а) Majestio - 5.7.2013, 13:13
PM MAIL WWW   Вверх
Guinness
Дата 5.7.2013, 14:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 21.6.2009
Где: Зеленоград

Репутация: 2
Всего: 10



Majestio, да, к несчастью, в qt не принято использовать исключения. Однако, Вы не пробовали использовать сигналы? Я вот тоже сейчас подошёл к этой проблеме. Сделал некий базовый класс Error, а дальше думаю от него наследоваться, что реализовать все необходимые ошибки( как в Java ). Вот эту ошибку у меня будет сигнал перекидывать. Конечно, для большой вложенности придётся делать много реконнектов, но я пока другого пути не нашёл.
И ещё, я постарался такие действия выделить в команды. В них могут происходить любые исключения, они могут запускаться в отдельном потоке.
Интерфейс, в общем-то, простой:
Код

class Command : public QObject, public QUndoCommand
{
signals:
    void finished();
    void error(const Error&);
    void stepName(const QString &);
    void stepProgress(int);
}

PM MAIL   Вверх
Majestio
Дата 5.7.2013, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 83
Регистрация: 19.12.2010

Репутация: нет
Всего: нет



Цитата(Guinness @ 5.7.2013,  14:06)
Majestio, да, к несчастью, в qt не принято использовать исключения. Однако, Вы не пробовали использовать сигналы? Я вот тоже сейчас подошёл к этой проблеме. Сделал некий базовый класс Error, а дальше думаю от него наследоваться, что реализовать все необходимые ошибки( как в Java ). Вот эту ошибку у меня будет сигнал перекидывать. Конечно, для большой вложенности придётся делать много реконнектов, но я пока другого пути не нашёл.
И ещё, я постарался такие действия выделить в команды. В них могут происходить любые исключения, они могут запускаться в отдельном потоке.
Интерфейс, в общем-то, простой:
Код

class Command : public QObject, public QUndoCommand
{
signals:
    void finished();
    void error(const Error&);
    void stepName(const QString &);
    void stepProgress(int);
}

Если честно, я не понял какой вопрос решает обработка ошибок используя сигналы.

Получая сообщение об ошибке в идеале нужно знать:

1) В каком именно месте кода она вылелела
2) Какая именно ошибка (описание)
3) Каковы условия ее возниконовения - стек вызовов, значения переданных параметров

Как в случае сигналов это можно реализовать?
PM MAIL WWW   Вверх
Guinness
Дата 5.7.2013, 14:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 21.6.2009
Где: Зеленоград

Репутация: 2
Всего: 10



Цитата(Majestio @  5.7.2013,  15:17 Найти цитируемый пост)
Как в случае сигналов это можно реализовать?

Создаётся класс Error, с параметрами, которые необходимы Вам для дальнейшей обработки. Потом Вы этот Error швыряете, а класс обработчик обрабатывает полученный Error.
Или я что-то не так объясняю?
PM MAIL   Вверх
bsa
Дата 5.7.2013, 14:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 24
Всего: 196



Мне вот интересно, а кто мешает сделать:
Код
int main(int argc, char *argv[])
   QApplication app(argc, argv);
....
   int result;
   for(;;) {
     try {
      result = app.exec();
      break;
     } catch(MyException &e) {
        QMessageBox::critical(...);
     } catch(std::exception &e) {
     }
  }
  return result;
А потом в коде кидать исключения.
PM   Вверх
Majestio
Дата 5.7.2013, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 83
Регистрация: 19.12.2010

Репутация: нет
Всего: нет



Ну если бы маленький примерчик ...

К примеру ....

1) Есть класс А, в его методе создается массив временных объектов класса В
2) В одном из созданных объектов (класса В) вызывается метод, который создает массив временных объектов класса С
3) В одном из созданных объектов (класса С) вызывается метод, который выполняется с ошибкой, к примеру "Попытка отката к несушествующей точке сохранения транзакции"

Как используя предложенный вами способ определить какой именно метод (и его параметры) объекта класса В вызвали ошибку, где это в программе. Равно как и А.
PM MAIL WWW   Вверх
Guinness
Дата 5.7.2013, 14:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 21.6.2009
Где: Зеленоград

Репутация: 2
Всего: 10



Цитата(bsa @  5.7.2013,  15:42 Найти цитируемый пост)
А потом в коде кидать исключения.

Я где-то на qt-project.org читал, что это не особо хорошо. Там прям длинное обсуждение на тему исключений в qt было. И этот метод не особо рекомендовали.
Пардон, думал Вы про переопределение в QApplication::notify.

Добавлено @ 15:02
Majestio, я сейчас понял, что Вас не устраивает использование __FILE__ и __LINE?

Это сообщение отредактировал(а) Guinness - 5.7.2013, 15:03
PM MAIL   Вверх
borisbn
Дата 5.7.2013, 16:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 48
Всего: 135



<Бесполезный совет>
Переходи на Java.  smile  Там будет примерно как посоветовал bsa, но можно даже не ловить исключение. Java сама запишет в свой log какое было исключение, в каком файле и на какой строке и плюс, естественно, весь стек вызова.
</Бесполезный совет>


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
Guinness
Дата 5.7.2013, 20:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 21.6.2009
Где: Зеленоград

Репутация: 2
Всего: 10



Цитата(borisbn @  5.7.2013,  17:50 Найти цитируемый пост)
Переходи на Java.

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

Majestio, если это Вас устроило, то я так задумывал это:
Код

if(isError){
     ConcreteError err( errId, __FILE__, __DATE__, __LINE__, someParam);
     emit error(err);
}

Далее в классе выше можно сделать connect( &obj, SIGNAL( error( const Error& ) ), this, SIGNAL( error( const Error& ) ) ).
Пока не дойдете до класса, который умеет обрабатывать данный сигнал. Это как бы я описал, примерно как я у себя собираюсь сделать.
Но мне почему то кажется, что я не понял какая у Вас проблема.
PM MAIL   Вверх
bsa
Дата 7.7.2013, 18:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 24
Всего: 196



кидать исключения можно через макрос, который будет дописывать файл и строку к сообщению об ошибке:
Код
#define THROW_ERROR(message) throw std::runtime_error(std::string(message) + " (" + __FILE__ + ":" + __LINE__)

PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Кроссплатформенное программирование, QT/Gtk+/wxWidgets"
JackYF
Любитель
  • В заголовке темы в квадратных скобках обозначьте используемую вами библиотеку, например: [QT],[GTK],[wx].
  • Если вопрос актуален только для некоторой версии библиотеки, либо, если вы пользуетесь не самой последней версией, укажите это. Например: [QT4], [GTK2].
  • Все начинающие изучать Qt - не забудьте зайти сюда.
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • В вопросе укажите полную версию версию библиотеки, а также все дополнительные используемые программные пакеты.
  • Не забывайте пользоваться кнопкой "Код".
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к тематике этого раздела. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, Любитель.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets | Следующая тема »


 




[ Время генерации скрипта: 0.0664 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.