Отладочные макросы



Visual C++ (как и многие другие среды разработки) содержит макросы, предназначенные специально для отладки. Например, макросы TRACE(), ASSERT() и VERIFY() приносят огромную пользу в процессе разработки. Они предназначены для разных целей, но у всех трех макросов есть нечто общее — они не отягощают итоговый выполняемый файл. При построении отладочных версий вашей программы макросы ведут себя так, как им положено. Однако в «окончательной» версии они удаляются из программы (вместе с теми возможностями, которые ими обеспечивались).
Макрос TRACE() посылает диагностические сообщения в окно отладчика. Правильно составленные сообщения образуют протокол событий, который можно просмотреть после выполнения программы (а средства удаленной отладки, о которой мы вскоре поговорим, позволяют сделать это даже во время работы программы). Макросы TRACE(), содержащие коды возврата и описания ошибок, помогают отыскать источники багов в ваших программах. Макрос TRACE() в отличие от двух других нормально работает в полноэкранных приложениях DirectDraw, так что вы можете свободно пользоваться им (этот макрос регулярно встречается в программах на CD-ROM).
Макросы ASSERT() и VERIFY() очень похожи друг на друга, что часто приводит к недоразумениям. На самом деле между ними существует очень важное различие. В отладочных версиях макросы ASSERT() и VERIFY() работают одинаково. Оба макроса вычисляют выражение, переданное им в качестве аргумента, и прекращают работу программы с выводом сообщения в том случае, если это выражение оказывается равным нулю. Отличие заключается в том, что в окончательной версии макрос ASSERT() вместе с вычисляемым выражением полностью удаляется из кода. С другой стороны, выражение макроса VERIFY() остается в программе. Макрос ASSERT() предназначен для проверки состояния переменных, а VERIFY() — для проверки функций. Выражение макроса ASSERT() относится только к состоянию переменных и не является обязательной частью программы, поэтому его можно удалить из окончательной версии.
Использование макросов ASSERT() и VERIFY() в полноэкранных приложениях DirectDraw осложняется тем, что при неудачной проверке выводится диалоговое окно. Несовместимость палитры может привести к искажению окна, а из-за переключения страниц окно может и вовсе не появиться на экране.
При таких затруднениях у вас есть два варианта: отказаться от ASSERT() и VERIFY() или предоставить нестандартные версии, работающие в DirectDraw. Второй вариант предпочтительнее, и, как выясняется, он реализуется достаточно просто.
Если покопаться в заголовочных файлах MFC, вы увидите, что в отладочном режиме макрос ASSERT() определяется так:

#define ASSERT(f) do { if (!(f) && AfxAssertFailedLine(THIS_FILE, __LINE__)) AfxDebugBreak(); } while (0

Выглядит довольно странно. Но вместо того, чтобы пытаться расшифровать логику его работы, мы согласимся с тем, что макрос работает, и попытаемся изменить его так, чтобы он правильно работал в приложениях DirectDraw. Однако перед этим следует заметить, что вывод диалогового окна и завершение приложения выполняются с помощью вызова AfxAssertFailedLine(). Следовательно, любой код, добавленный в этот условный оператор (что на первый взгляд кажется логичным), выполняться не будет.
Теперь давайте подумаем, что нужно сделать для нормального отображения диалогового окна. Можно вызвать функцию DirectDraw FlipToGDISurface() и обеспечить вывод диалогового окна на первичной поверхности, но проблема с палитрой при этом остается, к тому же окно может быть выведено в неверном видеорежиме. Вместо этого мы воспользуемся функцией RestoreDisplayMode() — это гарантирует вывод диалогового окна, активизацию стандартной палитры Windows и возврат к исходному видеорежиму Windows. Видоизмененный код выглядит так:

#define ASSERT(f) do { if (!(f)) { if (GetDDWin()) { GetDDWin()->GetDDraw()->RestoreDisplayMode(); GetDDWin()->GetDDraw()->Release(); } AfxAssertFailedLine(THIS_FILE, __LINE__); AfxDebugBreak(); } } while (0)

Для работы с объектом DirectDraw применяются функции GetDDWin() и GetDDraw() (которые соответственно возвращают указатели на объект DirectDrawWin и интерфейс DirectDraw). Помимо вызова функции RestoreDisplayMode() мы для приличия освобождаем объект DirectDraw. Также обратите внимание на перестановку, в результате которой наш код будет выполняться перед вызовом функции AfxAssertFailedLine().
Перейдем к макросу VERIFY(). Возвращаясь к заголовочным файлам MFC, мы находим, что в отладочной версии VERIFY() реализуется с помощью макроса ASSERT():

#define VERIFY(f) ASSERT(f)

Вспомните — в отладочной версии ASSERT() и VERIFY() ведут себя одинаково. Раз ASSERT() и VERIFY() реализуются одним макросом, VERIFY() можно оставить без изменений. Сказанное относится и к окончательным версиям макросов ASSERT() и VERIFY(), потому что нам не потребуется изменять их поведение. При компиляции окончательной версии ASSERT() и VERIFY() определяются так:

#define ASSERT(f) ((void)0) #define VERIFY(f) ((void)(f))

Как было сказано выше, выражение, передаваемое макросу ASSERT(), удаляется из окончательной версии (в действительности оно заменяется выражением ((void)0), которое игнорируется компилятором). Выражения, передаваемые VERIFY(), остаются в коде программы, однако их значение больше не проверяется.
Нам остается лишь переопределить стандартный вариант ASSERT() из MFC своим нестандартным вариантом. Для этого необходимо сначала отменить определение макроса из MFC. Кроме того, нужно позаботиться о том, чтобы подстановка осуществлялась только в отладочной версии. Окончательный код выглядит так:

#ifdef _DEBUG #undef ASSERT #define ASSERT(f) do { if (!(f)) { if (GetDDWin()) { GetDDWin()->GetDDraw()->RestoreDisplayMode(); GetDDWin()->GetDDraw()->Release(); } AfxAssertFailedLine(THIS_FILE, __LINE__); AfxDebugBreak(); } } while (0) #endif _DEBUG

Модифицированный макрос находится на CD-ROM и готов к работе, поэтому если вы захотите внести изменения в какую-нибудь программу, то можете свободно пользоваться макросами TRACE(), ASSERT() и VERIFY(). Кроме того, этот код автоматически генерируется и включается в проекты, создаваемые DirectDraw AppWizard.


Содержание Назад Вперед