В тех случаях, когда в отладочной версии не хватает стандартных инструментов (TRACE/ATLTRACE, assert/ASSERT/ATLASSERT), приходится писать блоки кода в обрамлении
#ifdef _DEBUG
...
#endif
что не добавляет красоты, но потенциально плодит ошибки (компилятору абсолютно пофиг, где стоит #endif).
Макросы IS_DEBUG и DEBUG_ONLY позволяют свести эти изыски к синтаксису языка Си.
// IS_DEBUG - булева константа, истинна если собирается DEBUG-версия#ifndef IS_DEBUG
#if defined(DEBUG) || defined(_DEBUG) // проверяем оба признака#define IS_DEBUG true
#else
#define IS_DEBUG false
#endif
#endif// DEBUG_ONLY - префикс оператора, который выполнится только в DEBUG-версии
// синтаксис - аналогичен составному оператору while()#ifndef DEBUG_ONLY
#define DEBUG_ONLY if(!(IS_DEBUG)) {} else// такая конструкция нужна, чтобы не конфликтовать с внешним if#endif// аналогично ему - RELEASE_ONLY
// хотя зачем он может пригодиться - не знаю; пусть будет.#ifndef RELEASE_ONLY
#define RELEASE_ONLY if(IS_DEBUG) {} else
#endif
Пример использования
// одиночный оператор
DEBUG_ONLY fprintf(log, "Контрольная точка 1.\n");
// составной оператор
DEBUG_ONLY
{
FILE* log = fopen("c:/log.txt", "a");
fprintf(log, "Контрольная точка 2.\n");
fclose(log);
}
// использование в выражениях
printf("Вас приветствует CoolApp (ver.1.2.%s)\n", IS_DEBUG ? "beta" : "rc1");
int i = (!IS_DEBUG) * rand(); // в дебаге - всегда 0, в релизе - случайное число
Здравствуйте Кодт, Вы писали:
К>В тех случаях, когда в отладочной версии не хватает стандартных инструментов...
Что-то мне это не нравится с одной стороны. С другой стороны — красиво. Не нравится тем, что при использовании DEBUG_ONLY в release версии все равно будет присутствовать мертвый код, и будет jmp для его обхода — и не надо надеяться на оптимизатор! К тому же, будут дополнительные предупреждения компилятора типа "code can never be reached" — я не говорю о MS C++, я говорю о грамотных C++ компиляторах вообще. В общем, у меня противоречивое мнение — "элегантно, но грязно"
McSeem
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте McSeem2, Вы писали:
MS>Что-то мне это не нравится с одной стороны. С другой стороны — красиво. Не нравится тем, что при использовании DEBUG_ONLY в release версии все равно будет присутствовать мертвый код, и будет jmp для его обхода — и не надо надеяться на оптимизатор!
Почему?
MS>К тому же, будут дополнительные предупреждения компилятора типа "code can never be reached" — я не говорю о MS C++, я говорю о грамотных C++ компиляторах вообще. В общем, у меня противоречивое мнение — "элегантно, но грязно"
В финальном релизе можно повыкидывать код.
Например, определить макросы
Здравствуйте Кодт, Вы писали:
К>Здравствуйте McSeem2, Вы писали:
MS>>Что-то мне это не нравится с одной стороны. С другой стороны — красиво. Не нравится тем, что при использовании DEBUG_ONLY в release версии все равно будет присутствовать мертвый код, и будет jmp для его обхода — и не надо надеяться на оптимизатор!
К>Почему?
Потому что Практика показывает, что на него никогда не надо надеяться. И уж тем более на то, что этот код будет выкинут. Впрочем, для определенных, некритических случаев это не важно.
Но скажем, release версия вообще не использует CRT ни под каким видом. Так при данном способе, что-то сомнительно, чтобы компилятор решился выкинуть такой код, который вызывает fprintf, черта лысого и сбоку бантик. И чтобы линкер этот код не прилинковал
К>К сожалению, С++ не позволяет делать мета-макросы, вида К>
К>(хотя, может быть, в C99 уже есть что-то такое?)
В C вообще препроцессор убогий, что довольно странно. Веди ноги-то растут из PDP-7 (если не ошибаюсь), на котором уже в те времена был весьма развитой макро-ассемблер. Ключевое слово здесь "макро". В общем, можно было бы и перенять опыт для развитого препроцессора.
McSeem
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>Что-то мне это не нравится с одной стороны. С другой стороны — красиво. Не нравится тем, что при использовании DEBUG_ONLY в release версии все равно будет присутствовать мертвый код, и будет jmp для его обхода — и не надо надеяться на оптимизатор! К тому же, будут дополнительные предупреждения компилятора типа "code can never be reached" — я не говорю о MS C++, я говорю о грамотных C++ компиляторах вообще. В общем, у меня противоречивое мнение — "элегантно, но грязно"
А я обычно такие макросы использую:
#ifdef _DEBUG
#define IF_DEBUG(do) do
#define IF_NODEBUG(do)
#else
#define IF_DEBUG(do)
#define IF_NODEBUG(do) do
#endif
В них вышеперечисленые проблемы отсутствуют, но при этом менее красиво.
К>// one(); two(); three(); // two(), three() не попали в релиз
К>// // бессмысленный...
К>{
К> one(); two(); three();
К>}
К>// { // ... и беспощадный
К> one(); two(); three();
К>}
К>
Да уж блин, слона-то я и не приметил Ну вас с вашими DEBUG_ONLY, ради DEBUG_LINE и заморачиваться не стоило, держать в релизе никогда не выполняющийся отладочный код тоже не фонтан. Я уж лучше по старинке, с #ifdef'ами буду дальше мучаться.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
$>> ICL -Fa test.cpp
KP> PUBLIC ?code@@YAXXZ KP>?code@@YAXXZ PROC NEAR KP>$B1$1: ; Preds $B1$0 KP> ret ;5.1
KP>И чго тут линкер увидет ? вызов printf ?
Глупый ответ. Компилятор не обязан это делать, понимаешь, не обязан. Тот факт, что Intel это выкинул, отнюдь не гарантирует того, что все другие компиляторы поступят так же. Нету здесь никаких гарантий, и соответсвенно, не на что надеяться (если, конечно пишется программа ВООБЩЕ, а не программа для конкретного компилятора с конкретными ключами оптимизации).
Здесь я сам не знаю, как правильно поступить. С одной стороны, можно выкинуть все нафиг. В другой — я ожидаю, что модуль, в котором определена my_variable будет прилинкован, а для этого надо оставить данный код. И не надо говорить, что если не используешь, то и не надо. Прилинковать бывает надо, особенно в случае полиморфных классов со сложным наследованием.
В этом, кстати, есть еще одна опасность. Возьмем фабрики классов, реализованные по типу MFC, ну там, макросы типа DECLARE_DYNAMIC (или как они там — не помню уже). Так вот, в DEBUG все работает, в RELEASE — падает, потому что компилятор выкинул обращение к классу, а линкер его не прилинковал. В результате получаем null-pointer VMT. В общем, данный подход чреват примерно теми же последствиями, что и ASSERT(a = fgets(buf, 100, fd)), но только в гораздо более редких и гораздо более трудноуловимых случаях.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.