Ошибки, Ошибки, Ошибки ...

Ну вот мы с вами делаем второй шаг на пути к нашей игре. Честно признаюсь, что я не представлял себе тот объем работы, с которым мне пришлось столкнуться. Перечитав по три раза Бьерна Страуструпа и собрав вместе беспорядочно бродившие мысли, я стал программировать:

Обработчик ошибок

Да-да, начал я именно с обработчика ошибок. Проект предстоит довольно большой, и поэтому ошибки неизбежно будут возникать, и для нас же лучше заранее продумать стратегию их обнаружения и исправления (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;
}

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

Вот такое "красивое" окно может появитсья в процессе работы программы.

 

Сайт создан в системе uCoz