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

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

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 ответов


С переменным макросом:

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

#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);
    }
}

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

сохранить его простым. Передайте измененную строку формата в 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);
    }
}

во-первых, я не думаю, что есть способ, чтобы распечатать значение 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);
    }
}

поскольку вы исключили это в комментариях, я предполагаю, что это должно сохранить функцию general и только выбранные программы печатают идентификатор потока. Я даю вам следующее чудовище:

#define t_printf(format, ...) t_printf("%p: " format, (void*)pthread_self(), __VA_ARGS__)

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