Подтвердить что ты не робот

Как добавить новый аргумент в существующий список аргументов переменных?

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

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?

4b9b3361

Ответ 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__)

Он работает только в том случае, если строка формата является литералом, и я не рекомендую использовать ее в производственном коде, но это просто "взломать", чтобы выполнить эту работу.