Ошибки, Ошибки, Ошибки ...
Ну вот мы с вами делаем второй шаг на пути к нашей игре. Честно признаюсь, что я не представлял себе тот объем работы, с которым мне пришлось столкнуться. Перечитав по три раза Бьерна Страуструпа и собрав вместе беспорядочно бродившие мысли, я стал программировать:
Обработчик ошибок
Да-да, начал я именно с обработчика ошибок. Проект предстоит довольно большой, и поэтому ошибки неизбежно будут возникать, и для нас же лучше заранее продумать стратегию их обнаружения и исправления (Good bug - dead bug :)). И появился новый класс, с нехитрым названием - tError:
/*--------------------------------------------------------------
tError Класс обработчика ошибок
--------------------------------------------------------------*/
class TENGINE_DLL tError
{
char *m_errorFile; // Имя файла, где произошла ошибка
int m_errorLine; // Строка, где произошла ошибка
char *m_errorHistory; // История ошибки
char *m_errorMessage; // Сообщение об ошибке
public:
tError(const char *file,int line,const char *history,const
char *message ...);
virtual ~tError();
//
void AddToHistory(const char *history) {strcat(m_errorHistory,history);};
//
void Message(void);
const char *GetMessage(void);
};
Как вы сами понимаете, этот класс создается при обнаружении в программе ошибки, храня в себе исчерпывающию информацию о ней. Хочется обратить внимание на конструктор этого класса, в котором возможно передавать неопределенное количество параметров:
/*--------------------------------------------------------------
tError::tError()
Конструктор, инициализирущий ошибку
--------------------------------------------------------------*/
tError::tError(const char *file,int line,const char *history,const char *message
...)
{
m_errorFile = new char[256];
m_errorHistory = new char[4096];
m_errorMessage = new char[1024];
//
strcpy(m_errorFile,file);
m_errorLine = line;
strcpy(m_errorHistory,history);
//
va_list list;
va_start(list,message);
vsprintf(m_errorMessage,message,list);
va_end(list);
}
Это необходимо для передачи отформатированного собщения об ошибке, подобно функции printf, например:
tError(__FILE__,__LINE__,"", "Ошибка №%x,0x00ff);
Поверьте, очень удобно.
Обработка исключительных ситуаций
Ну вот, класс ошибок у нас теперь есть, осталось только применить его на практике. Вторым классом будет у нас tFile - класс файла (он нам пригодится нам в дальнейшем). Сами понимаете, что при открытии и закрытии файла могут возникать различные ошибки, о которых сразу лучше знать. Для этого в функциях этого класса введем вызовы исключительных ситуаций:
/*--------------------------------------------------------------
tFile::OpenFile()
Открытие файла
--------------------------------------------------------------*/
void tFile::OpenFile(const char *name,int flags)
{
char *openFlags = new char[8];
try {
// Закрыть файл
tFile::CloseFile();
}
catch(tError &error) {
error.AddToHistory(" <-tFile::OpenFile");
throw;
}
// Копируем имя файла
strcpy(m_fileName,name);
// Копируем флаги
m_fileFlags = flags;
// Формирование флагов для открытия
if(m_fileFlags&TFILEREAD)
strcpy(openFlags,"r");
if(m_fileFlags&TFILEWRITE)
strcpy(openFlags,"r+");
if((m_fileFlags&TFILEWRITE)&&(m_fileFlags&TFILENEW))
strcpy(openFlags,"w");
if((m_fileFlags&TFILEREAD)&&(m_fileFlags&TFILENEW))
strcpy(openFlags,"w+");
if(m_fileFlags&TFILEBINARY)
strcat(openFlags,"b");
else
strcat(openFlags,"t");
// Открытие файла
m_file = fopen(m_fileName,openFlags);
if(!m_file) throw tError(__FILE__,__LINE__,"tFile::OpenFile",
"Can't
open file! / file - %s / flags - 0x%x",m_fileName,m_fileFlags);
}
Как видите, в последеней строке этой функции, командой throw вызывается исключительная ситация, которая сигнализирует о возникновении ошибки в программе. Перехватывается она обработчиком исключительных ситуаций, образованный конструкцией try{} catch(){}:
int APIENTRY WinMain(HINSTANCE
hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
try {
tFile *file = new tFile("new
text.txt",TFILEREAD);
}
catch(tError &error) {
error.AddToHistory(" <-WinMain
<-game.exe");
error.Message();
return 1;
}
return 0;
}
В результате, появилась возможность не только перехватывать ошибки и показывать цепочку вызовов, приведшие к ней, но и обнаруживать ошибки в конструкторах классов.
Вот такое "красивое" окно может появитсья в процессе работы программы.