Как добавить новый аргумент в существующий список аргументов переменных?
В многопоточной программе я пишу пользовательскую функцию печати, которая принимает список переменных.
void t_printf(char * str, ...)
{
if(file_ptr != NULL)
{
va_list ap;
va_start(ap, str);
vfprintf(file_ptr, str, ap);
va_end(ap);
fflush(file_ptr);
}
}
Внутри этой функции я хочу добавить текущий идентификатор потока (используя pthread_self()
) к печатаемому сообщению. Как мне это сделать? Есть ли способ добавить его в существующий va_list?
Ответы
Ответ 1
С переменным макросом:
С переменным макросом вы можете вызвать функцию с аргументом, добавленным или добавленным:
#define t_printf(format, args...) \
_t_printf(format, thread_id, __VA_ARGS__);
Это добавляет thread_id
перед другими аргументами. (Обратите внимание, что в функции _t_printf()
вы также должны изменить строку формата.)
Если вы это сделаете:
t_printf("some format string", a, b, c);
Это будет расширяться:
_t_printf("some format string", thread_id, a, b, c);
Если t_printf()
вызывается без другого аргумента, который форматирует, у вас будет конечная запятая. GCC имеет расширение ##
, которое заботится о добавлении запятой по мере необходимости:
#define t_printf(format, args...) \
_t_printf(format, thread_id ##__VA_ARGS__);
Полное решение с макросом:
#define t_printf(format, args...) \
_t_printf(format, thread_id, __VA_ARGS__);
void _t_printf(char * str, ...)
{
if(file_ptr != NULL)
{
char format[1024];
/* safely prefix the format string with [thread_id: %x] */
snprintf(format, sizeof(format), "%s%s", "[thread_id: %x] ", str);
va_list ap;
va_start(ap, str);
vfprintf(file_ptr, format, ap);
va_end(ap);
fflush(file_ptr);
}
}
Без изменения аргументов
Другое решение - сделать два printf() s:
vsnprintf(buffer, bufsize, str, ap);
vfprintf(file_ptr, "[thread_id: %x] %s", thread_id, buffer);
Полное решение:
void _t_printf(char * str, ...)
{
if(file_ptr != NULL)
{
char buffer[1024];
va_list ap;
va_start(ap, str);
vsnprintf(buffer, sizeof(buffer), str, ap);
vfprintf(file_ptr, "[thread_id: %x] %s", thread_id, buffer);
va_end(ap);
fflush(file_ptr);
}
}
Ответ 2
Я считаю, что нет стандартного способа манипулирования va_list.
Заголовок stdarg.h определяет макросы, чтобы объявлять, инициализировать, копировать, завершать списки и извлекать аргумент (различающийся тип зависит от вызывающего).
Здесь предложение альтернативы для достижения того же результата:
Обратите внимание, что это накладывает ограничение на формат строки. В зависимости от того, что вы хотите, это может быть не проблема:
#define MAXLEN 256
void t_printf(char * str, ...)
{
if(file_ptr != NULL)
{
va_list ap;
va_start(ap, str);
char msg[MAXLEN];
vsnprintf( msg , MAXLEN , str , ap ); /* msg is guaranteed
* to be NULL terminated
*/
/* Now that we have the message printed into a string,
* print the message, along with the thread_id into the
* console
*/
fprintf( file_ptr, "thread % 6d: %s", pthread_self() , msg );
va_end(ap);
fflush(file_ptr);
}
}
Ответ 3
Держите его простым. Передайте строку измененного формата в vfprintf:
void t_printf(char * str, ...)
{
if(file_ptr != NULL)
{
char fmt[MAXLEN];
va_list ap;
va_start(ap, str);
// amend format and insert thread id
snprintf(fmt, MAXLEN, "thread id: %d: %s", pthread_self(), str);
vfprintf(file_ptr, fmt, ap);
va_end(ap);
fflush(file_ptr);
}
}
Ответ 4
Во-первых, я не думаю, что там поддерживается способ печати значения pthread_self
(ref: существование pthread_equal
), здесь я передаю его void*
и используя "% p" - измените, как вы сочтете нужным.
Поскольку, к сожалению, вы не можете добавить к va_list
, я бы обычно делал что-то вроде этого:
void t_printf(char * str, ...)
{
if(file_ptr != NULL)
{
va_list ap;
va_start(ap, str);
#if defined(USING_THREADS) && defined(LOGGING)
fprintf(file_ptr, "%p", (void*)pthread_self());
#endif
vfprintf(file_ptr, str, ap);
va_end(ap);
fflush(file_ptr);
}
}
Поскольку вы исправили это в комментариях, я предполагаю, что это означает, что общая функция и только выбранные программы распечатывают идентификатор потока. Я даю вам следующее чудовище:
#define t_printf(format, ...) t_printf("%p: " format, (void*)pthread_self(), __VA_ARGS__)
Он работает только в том случае, если строка формата является литералом, и я не рекомендую использовать ее в производственном коде, но это просто "взломать", чтобы выполнить эту работу.