Как я могу добавить новый аргумент в существующий список аргументов переменной? - PullRequest
16 голосов
/ 29 августа 2011

В многопоточной программе я пишу пользовательскую функцию печати, которая принимает переменный список аргументов.

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?

Ответы [ 4 ]

20 голосов
/ 29 августа 2011

С помощью макроса с переменным числом:

С помощью макроса с переменным значением можно вызвать функцию с добавленным или добавленным аргументом:

#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 ():

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);
    }
}
5 голосов
/ 29 августа 2011

Я считаю, что нет стандартного способа манипулировать списком 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 голосов
/ 29 августа 2011

Будьте проще.Передайте измененную строку формата в 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);
    }
}
1 голос
/ 30 августа 2011

Во-первых, я не думаю, что есть поддерживаемый способ печати значения pthread_self (ссылка: существование 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__)

Это работает только в том случае, если строка формата является литералом, и я не рекомендую использовать его в производственном коде, но это легкий «взлом» для выполнения работы.

...