LifeMaker,
дык это же сильно неудобно, при каждом вызове функции обрамлять её #idef'ом.
зачем делать 100 раз то, что можно сделать один раз?
и всех коллег заставлять? (я ведь не один программист на проекте) и каждый вечер просматривать весь проект, проверять, чтоб никто не забыл #idef поставить. и всё равно забудут где-нить поставить и я не замечу.
вот для этого и это все обернуто в функцию (точнее конструктор), да ее вызов и создание объекта на стеке надо будет стерпеть (потеря производительности на лицо, но если это не критично). Ну а параметры вычисляй уже внутри ifdef (если это не возможно, то и макрос тебя не спасет от их вычислений)
В класс лучше оборачивать чем просто в функцию много потенциальных бонусов при минимуме затрат
я смотрю мы немного недопонимаем друг друга... давай лучше я приведу свой код и объяню, почему я использую макрос и ничто другое.
задача написать вспомогательное нечто, позволяющее легко, не заморачиваясь выводить в уже открытый стандартный FILE* некую форматную строку, возможно не только в файл, но и в Debug Output Visual Studio, TTY игровую консоль, куда угодно в общем-то...
иметь возможность отключить всё это не имея при этом оверхеда (это игровое приложение), в Release-билде каждый FPS на счету.
я могу поместить WRITE_LOG в цикл рендера, там даже вызов пустой функции или пустой конструир объекта с парой параметров может очень сильно подпортить жизнь...
вот так:
WRITE_LOG("ptr is NULL");
WRITE_LOG("File '%s' not found.",filename.c_str());
WRITE_LOG("Players scores: '%d'.",mPlayerScores);
WRITE_LOG("Matrix value is: %f %f %f %f - %f %f %f %f - %f %f %f %f - %f %f %f %f",mtx[0][0],mtx[0][1],mtx[0][2],mtx[0][3],
mtx[1][0],mtx[1][1],mtx[1][2],mtx[1][3],mtx[2][0],mtx[2][1],mtx[2][2],mtx[2][3],mtx[3][0],mtx[3][1],mtx[3][2],mtx[3][3],);
WRITE_LOG("Player2 health: %f", gGameDataBase->FindPlayerNode("Player2")->GetHealth());
// реализация примерно так WRITE_LOG
#ifdef NDEBUG
#define WRITE_LOG(...)
#else
#ifdef WIN32
#define WRITE_LOG(...) { \
fprintf(gLogFile,__VA_ARGS__);fprintf(gLogFile,"\n"); \
char buf[512]; sprintf(buf,__VA_ARGS__); OutputDebugString("buf"); OutputDebugString("\n"); \
}
#endif
#ifdef PS3
#define WRITE_LOG(...) { \
fprintf(gLogFile,__VA_ARGS__);fprintf(gLogFile,"\n"); \
PSSG_PRINTF(__VA_ARGS__); PSSG_PRINTF("\n"); \
}
#endif
#endif
при таком подходе в релиз-билде не остаётся ни вызова пустой функции, ни вычисления параметров для формирования строки (как видно из примера - это может быть много параметров или вызовы тяжелых функций поиска и т.п.)
отдельный момент, ради которого используется макрос: проверка GCC компилятором корректности форматной строки.
т.е. вот что он говорит на вот таком коде:
1: // test.cpp
2: #include <stdio.h>
3: int main()
4: {
5: float f;
6: int i;
7: printf("%d %f",f,i);
8: return 0;
9: }
test.cpp: In function `int main()':
test.cpp:7: warning: int format, double arg (arg 2)
test.cpp:7: warning: double format, different type arg (arg 3)
полезная подсказка чаще всего программист ошибается вот так
std::string str;
printf("%s",str); //плохо
printf("%s",str.c_str()); //надо было так, все об этом знают, но часто забывают, а вылазит только в рантайме...
так вот, к чему это я... вариант с функцией выглядит примерно так...
void WRITE_LOG(const char* fmt, ...)
{
#ifndef NDEBUG
va_list argList;
va_start(argList, fmt);
char buffer[bufSize];
vsnprintf(buffer, bufSize, fmt, argPtr);
va_end(argList);
OutputDebugString(buffer);
OutputDebugString("\n");
#endif
}
в итоге GCC говорит что-то вроде warning: Format string is not literal. Unable to check parameters.
итого, мы потеряли ценную информацию от компилятора. в случае с макросом вся информация сохраняется, как буд-то мы передали параметры непосредственно в printf (что мы собственно и сделали). и warning уже выручал меня раза три, и коллег уже выручал, хотя WRITE_LOG'у этому всего неделя примерно... и выручит, я думаю, ещё не раз