Суть проблеммы вначале заключалась в том что бы писать форматированный вывод printfа помимо как в OUTPUT еще и в лог файл.
1) Сначала я хотел сделать это через функцию которая вызывает и fprint и printf, но обнаружил что чтобы это сделать необходимо определить число передаваемых параметров и их типы по строке форматирования... в итоге был напсин код на асме который успешно сохраняет кусочек стека в heapе а потом шаманит с указателем стека и вызывает printf и fprint. Все бы нечиго, я даже заделал макрос, который генерит соответсвующие asmные вставки, но по каким то причинам слово Assembler кроме меня никому не греет душу
2) Возникла идея, что раз нельзя что бы функция ето все тварила, то пусть хотя бы макрос, что ему стоит строчке в тексте слепить, но обнаружилось что препроцессор C++ такой ущербный язык, что кажется его таким сделал специально — 'ибо нефиг' . В результате нескольких попыток, был получен слегка корявый вариант, который все таки слепляет агрументы из двух списков для функции с переменным числом агрументов:
// Бубен :)#define __AST *
#define Cc __AST/ //Comment Close#define Co /__AST //Comment Open#define _FUNCCALL(func, h, t) func(Co h Cc, Co t Cc)
// Конец Бубна
// Целывой макрос
FILE* logFile = 0;
#define LOGPRINT(x) \
printf##x; \
if (logFile) _FUNCCALL(fprintf, (Cc logFile Co), x);
// Используется как
LOGPRINT((Cc "Error code: %d. Error Message: %s\r\n", errCode, errMsg Co))
ВОПРОС: Есть у кого идеи как всетаки сделать макрос LOGPRINT чтобы его можно было записать:
LOGPRINT(("Error code: %d. Error Message: %s\r\n", errCode, errMsg)), т.е. без Cc и Co.
а то не красиво смотрится
Кто подскажеть тому
18.08.05 11:38: Перенесено модератором из 'C/C++. Прикладные вопросы' — Павел Кузнецов
// Целывой макрос
FILE* logFile = 0;
#define LOGPRINT(x) \
printf( Co x Cc ); \ // здесь было printf##x
if (logFile) _FUNCCALL(fprintf, (Cc logFile Co), x);
Здравствуйте, rus blood, Вы писали:
RB>Здравствуйте, vnechiporenko, Вы писали:
V>>Кто подскажеть тому
RB>Напиши свою функцию с параметром-строкй и переменным числом аргументов, которая будет вызывать vfprintf. RB>Далее стандартно...
RB>#ifdef _DEBUG RB> #define LOGPRINT myFunc RB>#else RB> #define LOGPRINT 1 ? (void)0 : myFunc RB>#endif
RB>LOGPRINT("Error code: %d. Error Message: %s\r\n", errCode, errMsg);
Текст myFunc попробуй написать и увидишь что не сможешь определить количетсво переданных параметров и их типы (Ну если конечно не писать свой анализатор строки форматирование -- нормальному человеку это делать лень), а как следствие не сможешь их ретранслировать в fprintf. Ну к твоему облегчению я писал уже такую функцию, в ней я использовал вставки на asm'е.. (смотри (1) в моем первом посте). Если вкурсе как написать это без асма то получить много .
Здравствуйте, vnechiporenko, Вы писали:
V> Текст myFunc попробуй написать и увидишь что не сможешь определить количетсво переданных параметров и их типы (Ну если конечно не писать свой анализатор строки форматирование -- нормальному человеку это делать лень), а как следствие не сможешь их ретранслировать в fprintf. Ну к твоему облегчению я писал уже такую функцию, в ней я использовал вставки на asm'е.. (смотри (1) в моем первом посте). Если вкурсе как написать это без асма то получить много .
Ты пишешь под абстрактного коня в вакууме, или под конкретную систему?
Здравствуйте, vnechiporenko, Вы писали:
V> Текст myFunc попробуй написать и увидишь что не сможешь определить количетсво переданных параметров и их типы (Ну если конечно не писать свой анализатор строки форматирование -- нормальному человеку это делать лень), а как следствие не сможешь их ретранслировать в fprintf. Ну к твоему облегчению я писал уже такую функцию, в ней я использовал вставки на asm'е.. (смотри (1) в моем первом посте). Если вкурсе как написать это без асма то получить много .
Впрочем, неважно.
1. Функции типа vfprintf используют va_list для просмотра аргументов.
2. Если подобных функций нет, можно взять готовый код разбора аргументов. Смотри ATLString или MFC CString::Format
Имею скафандр — готов путешествовать!
Re[3]: Препроцессор и переменное число аргументов
От:
Аноним
Дата:
18.08.05 07:51
Оценка:
Здравствуйте, vnechiporenko, Вы писали:
в принципе ссылка уже дана, но на всякий случай
кому лень искать, должно быть примерно так
void Trace( const char *szFormat, ... )
{
va_list arg;
va_start( arg, szFormat ); // Initialize variable arguments
char sMessage[ 1024 ] = { '\0' };
::vsprintf( sMessage, szFormat, arg );
va_end( arg ); // Reset variable arguments
::strcat( sMessage, "\r\n" );
Здравствуйте, rus blood, Вы писали:
RB>1. Функции типа vfprintf используют va_list для просмотра аргументов.
Сори ступил Я только сейчас прочел про vfprintf (не знал о ё суфществовании), действительно рулит... Её спецом похоже для этих целей сделали.
RB>2. Если подобных функций нет, можно взять готовый код разбора аргументов. Смотри ATLString или MFC CString::Format
Угу.. и это дельный совет
Здравствуйте, vnechiporenko, Вы писали:
V>Суть проблеммы вначале заключалась в том что бы писать форматированный вывод printfа помимо как в OUTPUT еще и в лог файл.
V>ВОПРОС: Есть у кого идеи как всетаки сделать макрос LOGPRINT чтобы его можно было записать: V>LOGPRINT(("Error code: %d. Error Message: %s\r\n", errCode, errMsg)), т.е. без Cc и Co. V>а то не красиво смотрится
В библиотеке ACE это уже давно сделано. Вот пример логирования из их Basic_Types_Test.cpp:
Здравствуйте, vnechiporenko, Вы писали:
V>Сори ступил
Ничего страшного Ты не один.
Я тоже не знал о ее существовании, и тоже хотел логирование с форматной строкой сделать, как в printf. А выкрутился установкой исходника MS CRT и компиляцией модифицированной CRT! Был жутко доволен своей изобретательностью, пока не узнал про vprintf
Здравствуйте, pavel_turbin, Вы писали:
_>вроде бы в Linux-e (gcc?) такой наворот есть.
Не только есть, но и прекрасно используется
Макрос с переменным числом параметров — почти как функция.
Удобно — жуть как, незаменим для дебуга
Здравствуйте, arcman, Вы писали:
A>Не только есть, но и прекрасно используется A>Макрос с переменным числом параметров — почти как функция. A>Удобно — жуть как, незаменим для дебуга
Нет, это немного не то. Автор топика хочет, чтобы в дебажной версии были логи, а в релизной нет. Т.е. один дефайн/ундеф — и нету никакого логирования. Или тут тоже как-то можно?
И под C не покатит, так что может оно и лучше, но не так универсально.
Здравствуйте, Сергей, Вы писали:
WM>>IMHO, лучше пользоватся потоками. Определи что-нибудь вроде такого:
WM>>
WM>>log_print << "text: " << 5;
WM>>
С>Нет, это немного не то. Автор топика хочет, чтобы в дебажной версии были логи, а в релизной нет. Т.е. один дефайн/ундеф — и нету никакого логирования. Или тут тоже как-то можно?
Здравствуйте, vnechiporenko, Вы писали:
V>Суть проблеммы вначале заключалась в том что бы писать форматированный вывод printfа помимо как в OUTPUT еще и в лог файл.
[...] V>Кто подскажеть тому
А почему бы не сделать шаблонные заготовки ф-ий, до, скажем, 15-20 аргументов.
Здравствуйте, arcman, Вы писали:
A>Здравствуйте, pavel_turbin, Вы писали:
_>>вроде бы в Linux-e (gcc?) такой наворот есть. A>Не только есть, но и прекрасно используется A>Макрос с переменным числом параметров — почти как функция. A>Удобно — жуть как, незаменим для дебуга
что делает код непортируемый в Windows, т.к. cl не имеет такой поддержки.
Здравствуйте, Centaur, Вы писали:
C>Дальше, оптимизирующий компилятор все обращения к log_print в нелогирующей версии выоптимизирует до нуля.
Не факт.
Боюсь, оптимизатор может не справиться с таким случаем (для этого компилятору пришлось бы доказать, что ни одна из non_inline_functionX не имеет побочных эффектов; может быть, что глобальная оптимизация с этим и справится, но гарантии нет):
С>Нет, это немного не то. Автор топика хочет, чтобы в дебажной версии были логи, а в релизной нет. Т.е. один дефайн/ундеф — и нету никакого логирования. Или тут тоже как-то можно?
Тут тоже макросами можно.
#ifdef DEBUG
#define TRACE(x) get_trace_stream()<<x
#else
define TRACE(x)
#endif
...
int n;
TRACE("n=" << n);
// в этом случае немного неудобно, но жить можно
TRACE(("someFunc returned "<<someFunc(1,2,3)));
Здравствуйте, Gleb Alexeev, Вы писали:
С>>Нет, это немного не то. Автор топика хочет, чтобы в дебажной версии были логи, а в релизной нет. Т.е. один дефайн/ундеф — и нету никакого логирования. Или тут тоже как-то можно?
GA>Тут тоже макросами можно.
GA>
GA>#ifdef DEBUG
GA>#define TRACE(x) get_trace_stream()<<x
GA>#else
GA>define TRACE(x)
GA>#endif
GA>...
GA>int n;
GA>TRACE("n=" << n);
GA>// в этом случае немного неудобно, но жить можно
GA>TRACE(("someFunc returned "<<someFunc(1,2,3)));
GA>
А можно и по другому макросами (ноги растут отсюда):
Здравствуйте, Gleb Alexeev, Вы писали:
GA>Отличная идея , только я бы std::cout на какой-нибудь null_stream заменил бы (в случае, когда трассировка отключена).
Нет смысла. Ведь никуда ничего не выводится. Эта запись в случае !DEBUG будет эквивалентна:
std::cout;
А std::cout нужен для того, чтобы тип выражения в обоих ветках ?: был одинаковый -- std::ostream & (к примеру, или конкретный тип, к которому принадлежит std::cout).
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Gleb Alexeev, Вы писали:
GA>>Отличная идея , только я бы std::cout на какой-нибудь null_stream заменил бы (в случае, когда трассировка отключена).
E>Нет смысла. Ведь никуда ничего не выводится. Эта запись в случае !DEBUG будет эквивалентна: E>
E>std::cout;
E>
E>А std::cout нужен для того, чтобы тип выражения в обоих ветках ?: был одинаковый -- std::ostream & (к примеру, или конкретный тип, к которому принадлежит std::cout).
Я через секунду это и сам понял, только удалить не успел . Поставьте мне минус, кто-нибудь, и сорри за шум.
Здравствуйте, Gleb Alexeev, Вы писали:
GA>>>Отличная идея , только я бы std::cout на какой-нибудь null_stream заменил бы (в случае, когда трассировка отключена).
E>>Нет смысла. Ведь никуда ничего не выводится. Эта запись в случае !DEBUG будет эквивалентна: E>>
E>>std::cout;
E>>
E>>А std::cout нужен для того, чтобы тип выражения в обоих ветках ?: был одинаковый -- std::ostream & (к примеру, или конкретный тип, к которому принадлежит std::cout).
GA>Я через секунду это и сам понял, только удалить не успел . Поставьте мне минус, кто-нибудь, и сорри за шум.
Зачем же удалять?
Если что-то может быть истолковано не правильно, оно и будет истолковано неправильно ((С) кажется из законов Мерфи). Может еще у кого-то такой же вопрос возникнет, а тут сразу ответ готов.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Gleb Alexeev, Вы писали:
GA>Здравствуйте, Centaur, Вы писали:
C>>Дальше, оптимизирующий компилятор все обращения к log_print в нелогирующей версии выоптимизирует до нуля. GA>Не факт. GA>Боюсь, оптимизатор может не справиться с таким случаем (для этого компилятору пришлось бы доказать, что ни одна из non_inline_functionX не имеет побочных эффектов; может быть, что глобальная оптимизация с этим и справится, но гарантии нет): GA>
Мы всё ещё говорим о логировании, или уже перешли к минимизирующим поверхность при заданном объёме представителям семейства непарнокопытных в условиях отсутствия атмосферного давления? Какие неинлайновые функции, какие побочные эффекты? Да тут use case примерно следующей сложности:
logstream << "Hello world, we are flying at " << altitude << "m, "
"the temperatures of engines are " << writeContainer(engineTemp, ", ") << "\n";
Никаких неинлайновых функций, большей частью строковые литералы и локальные переменные, изредка конструкторы временных объектов, реализующих ленивую запись в поток. То есть, если нас попросят записаться в std::ostream, то мы запишемся в std::ostream, а если нас (сделают вид, что) запишут в null_stream без нашего участия, то мы и не будем ничего делать.
Здравствуйте, Centaur, Вы писали:
C>Мы всё ещё говорим о логировании, или уже перешли к минимизирующим поверхность при заданном объёме представителям семейства непарнокопытных в условиях отсутствия атмосферного давления? Какие неинлайновые функции, какие побочные эффекты? Да тут use case примерно следующей сложности: C>
C>logstream << "Hello world, we are flying at " << altitude << "m, "
C> "the temperatures of engines are " << writeContainer(engineTemp, ", ") << "\n";
C>
Никаких неинлайновых функций, большей частью строковые литералы и локальные переменные, изредка конструкторы временных объектов, реализующих ленивую запись в поток. То есть, если нас попросят записаться в std::ostream, то мы запишемся в std::ostream, а если нас (сделают вид, что) запишут в null_stream без нашего участия, то мы и не будем ничего делать.
Ну, во-первых, GetLastError у меня обычно вызывается сразу после возникновения ошибки, в конструкторе Win32Exception. Независимо от того, ведём мы логи или не ведём. А FormatMessage — соответственно в Win32Exception::what().
А во-вторых, даже в этом примере я попытаюсь максимально «ленивизировать» операцию — сделав lastErrorMsg классом и унеся всё тяжеловесное в его friend std::ostream& operator<<(std::ostream&, const lastErrorMsg&). В конструкторе останется один GetLastError(), и то только потому, что есть (минимальный) риск этот самый LastError прощёлкать клювом, успешно выполнив Win32API-операцию.
GA>При желании можно придумать и более правдоподобный случай, когда тяжеловесный код будет выполняться впустую при отключенной трассировке.
Здравствуйте, Centaur, Вы писали:
C>Ну, во-первых, GetLastError у меня обычно вызывается сразу после возникновения ошибки, в конструкторе Win32Exception. Независимо от того, ведём мы логи или не ведём. А FormatMessage — соответственно в Win32Exception::what().
C>А во-вторых, даже в этом примере я попытаюсь максимально «ленивизировать» операцию — сделав lastErrorMsg классом и унеся всё тяжеловесное в его friend std::ostream& operator<<(std::ostream&, const lastErrorMsg&). В конструкторе останется один GetLastError(), и то только потому, что есть (минимальный) риск этот самый LastError прощёлкать клювом, успешно выполнив Win32API-операцию.
С чего начали:
Замена log_stream на null_stream заставит оптимизатор полностью выкинуть код трассировки из приложения.
Чем закончили:
Я, нижеподписавшийся Centaur, лично никогда не вызываю не-inline функций при трассировке .
Удобство макросов типа ASSERT и TRACE в том, что их пишешь не задумываясь о стоимости, зная, что в релиз ничего из отладочного кода не попадет. Вы же призываете пользователя log_stream к дисциплине, ограничивая его удобство без особой на то причины (ниже ув. тов. eao197 дал более элегантное решение), тем более, что не-inline функция может не вызываться явно в выражении записи в поток.
C>Пока не убеждён
Ладно, другой пример:
С>Нет, это немного не то. Автор топика хочет, чтобы в дебажной версии были логи, а в релизной нет. Т.е. один дефайн/ундеф — и нету никакого логирования. Или тут тоже как-то можно? С>И под C не покатит, так что может оно и лучше, но не так универсально.