Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > va_args,__VA_ARGS__ и enum


Автор: flagist0 1.11.2007, 15:14
Есть функции вывода и логгирования ошибок - void error(int level, const char* msg, ...) и log.printf(const char *msg, ...). Обе принимают параметры в стиле printf - форматная строка и имена параметров.
Еще есть переменные int debug_lvl и log_lvl, хранящие уровни вывода (например, мы можем выводить только fatal error'ы и ничего не логгить). Уровни сообщений хранятся в enum DebugMsg{Disabled=-1,FatalError=0, Error,Warning,Message};
Нужна функция, которая будет в зависимости от текущих уровней вызывать error и log.printf. 
Например, void log_and_error(DebugMsg lvl,const char* msg), то есть принимаем уровень выводимого сообщения и УЖЕ скомпонованное с помощью sprintf'a сообщение;или void log_and_error(DebugMsg lvl,const char* msg, ...) - то же самое, но параметры мы должны передавать сами.

Соответственно, два вопроса на выбор  smile :

1.Чтобы не делать руками перед каждым вызовом log_and_error sprintf(expanded_msg,"Watch for %s added [descriptor=%d].\n",filenames[wd],wd)), я написал мааленький макрос, который это делает сам - 
#define _log_and_error(lvl,...) {sprintf(expanded_msg,__VA_ARGS__);log_and_error(lvl,expanded_msg);}
В нем используется __VA_ARGS__ (C99) и естественно ничего не работает. gcc (4.2.0) говорит :предупреждение: cannot pass objects of non-POD type ‘struct std::basic_string<char, std::char_traits<char>, std::allocator<char> >’ through ‘...’; call will abort at runtime  и в самом деле вылетает (Недопустимая инструкция). видимо дело в перечислении.

2. В случае log_and_error(DebugMsg lvl,const char* msg, ...), как же цивилизованно передать все параметры после lvl error и log?? Насколько я понял у Страуструпа, надо знать их тип, но мне не нужен разбор, мне нужно их просто передать функциям дальше!!

Заранее спасиб, извините за спам smile


Автор: JackYF 1.11.2007, 15:21
Цитата(flagist0 @  1.11.2007,  15:14 Найти цитируемый пост)
filenames[wd],wd

filenames[wd].c_str(), ws.c_str()

попробуй.


Вопрос в пустоту: а может, стоит отказаться от этой практики и заюзать boost::format - более прозрачное и типобезопасное решение?

Автор: xvr 1.11.2007, 15:30
Цитата(flagist0 @ 1.11.2007,  15:14)
Есть функции вывода и логгирования ошибок - void error(int level, const char* msg, ...) и log.printf(const char *msg, ...). Обе принимают параметры в стиле printf - форматная строка и имена параметров.
Еще есть переменные int debug_lvl и log_lvl, хранящие уровни вывода (например, мы можем выводить только fatal error'ы и ничего не логгить). Уровни сообщений хранятся в enum DebugMsg{Disabled=-1,FatalError=0, Error,Warning,Message};
Нужна функция, которая будет в зависимости от текущих уровней вызывать error и log.printf. 

Соответственно, два вопроса на выбор  smile :

1.Чтобы не делать руками перед каждым вызовом log_and_error sprintf(expanded_msg,"Watch for %s added [descriptor=%d].\n",filenames[wd],wd)), я написал мааленький макрос, который это делает сам - 
#define _log_and_error(lvl,...) {sprintf(expanded_msg,__VA_ARGS__);log_and_error(lvl,expanded_msg);}
В нем используется __VA_ARGS__ (C99) и естественно ничего не работает. gcc (4.2.0) говорит :предупреждение: cannot pass objects of non-POD type ‘struct std::basic_string<char, std::char_traits<char>, std::allocator<char> >’ through ‘...’; call will abort at runtime  и в самом деле вылетает (Недопустимая инструкция). видимо дело в перечислении.

2. В случае log_and_error(DebugMsg lvl,const char* msg, ...), как же цивилизованно передать все параметры после lvl error и log?? Насколько я понял у Страуструпа, надо знать их тип, но мне не нужен разбор, мне нужно их просто передать функциям дальше!!

В данном случае можно просто отформатировать строку самому, а дальше ее передать уже в виде строки целиком, а не в виде формата и набора параметров:
Код

void log_and_error(DebugMsg lvl,const char* msg, ...)
{
 va_list p;
 va_start(p,msg);
 string message=vprn(msg,p); // see 'utils.h'
 va_end(p);
 if (...) error(lvl,"%s",message.c_str());
 if (...) log.printf("%s",message.c_str());
}

Что касается передачи string в ... - то это работать будет, но не так, как ожидалось - спецификатор параметра %s в printf принимает строку с C стиле (char*), а string, да еще и переданный по значению, это совсем не то же самое smile

Автор: flagist0 1.11.2007, 16:50
Цитата

filenames[wd].c_str(), ws.c_str()

попробуй.

Извиняюсь, криво выразился. 
Код

sprintf(expanded_msg,"Watch for %s added [descriptor=%d].\n",filenames[wd],wd))
 - это просто пример формирования строки с ошибкой перед закидыванием ее в log_and_error.
Вопрос в том, как не загрязнять код sprintf'ами перед каждым выводом через log_and_error. Если бы sprintf возвращал бы указатель на сформированную строку, то я бы писал
Код

log_and_error(Message,sprintf(форматная_строка,прм1,прм2));

А приходится писать 
Код

sprintf(преобразованная_строка,"форматная_строка",прм1,прм2);
log_and_error(Message,преобразованная строка);

Я реализовал это в виде макроса _log_and_error, но ведь наверное есть лучший вариант?

Цитата

Вопрос в пустоту: а может, стоит отказаться от этой практики и заюзать boost::format - более прозрачное и типобезопасное решение?

Вполне возможно. Во всяком случае boost::lambda где-то в проекте уже используется. smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)