Вобщем вопрос встал совсем уж конкретно. Суть в следующем:
В простейших программах написал проверку, вывел сообщение, если есть возможность обработать, обработал. Все понятно и прозрачно. Но когда программа вырастает в достаточно большой проект, когда стек вызова вырастает до 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. Если метод класса отрабатывает с ошибкой, он в эту переменную пишет подробности. В коде использую макросы:
C++ (Qt)
#define GuiCheckContinue() if (Global.Error!="") { QMessageBox::warning(0, QObject::tr("Warning!"), Global.Error + QObject::tr("<hr>File: \"")+QFileInfo(__FILE__).fileName()+QObject::tr("\"<BR>Line: ")+QString::number(__LINE__)); qCritical() << Global.Error + QObject::tr(" File: \"")+QFileInfo(__FILE__).fileName()+QObject::tr("\" Line: ")+QString::number(__LINE__); Global.Error="";}
#define GuiCheckReturn() if (Global.Error!="") { QMessageBox::warning(0, QObject::tr("Warning!"), Global.Error + QObject::tr("<hr>File: \"")+QFileInfo(__FILE__).fileName()+QObject::tr("\"<BR>Line: ")+QString::number(__LINE__)); qCritical() << Global.Error + QObject::tr(" File: \"")+QFileInfo(__FILE__).fileName()+QObject::tr("\" Line: ")+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();
Выводится в случае ошибки нечто, типа:
Вобщем меня это как-то не устраивает. Какие есть мысли?
Вопрос 2. Обработка отката занятых ресурсов в случае ошибкиНе знаю почему, но в Qt почему-то не принято пользоваться исключениями.
Без них данный вопрос вообще выглядит каким-то монстром.
В приведенном выше коде, как вы заметите - с этим явно не чисто.
Я пока еще в раздумиях.
Если есть желание, подумайте над "простеньким" вопросом. Вот типичный алгоритм:
1) Открыли транзакцию
2) Заблокировали запись
3) Занесли информацию с служебную таблицу - кто заблокировал и на какую операцию
4) Отредактировали запись
5) Закоммитили транзакцию
6) Удалили инфу о вашей блокировке
7) Записали в журнал операций что вы сделали с записью
На шагах 1-2-4-5 используется первое соединение с базой, с 3-6-7 второе (назовем служебное). Чтобы "служебные" операции не попадали в транзакционный блок. Теперь представим случай, на шаге 5 связь с СУБД рвется, коммит не прошел. Что делать дальше?
Вощем буду рад любым идеям