如何在现有变量参数列表中添加新参数?

时间:2011-08-29 10:24:03

标签: c multithreading variadic-functions

在多线程程序中,我正在编写一个自定义打印函数,它接受一个可变参数列表。

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

在此函数中,我想将当前线程ID (使用pthread_self())添加到要打印的消息中。我该怎么做?有没有办法将它添加到现有的va_list?

4 个答案:

答案 0 :(得分:20)

使用可变参数宏:

使用可变参数宏,您可以使用前置或附加的参数调用该函数:

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

答案 1 :(得分:5)

我认为没有标准的方法来操纵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);
    }
}

答案 2 :(得分: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);
    }
}

答案 3 :(得分:1)

首先,我认为没有一种支持的方式可以打印pthread_self的值(参考:pthread_equal的存在),这里我将它转换为{{1}并使用“%p” - 根据需要进行更改。

很遗憾,您不能添加void*我通常会这样做:

va_list

由于你在评论中排除了这一点,我认为这是为了保持函数的一般性,并且只选择程序打印线程id。我给你以下怪物:

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

只有格式字符串是文字字符串才有效,我不建议在生产代码中使用它,但是完成工作很容易“破解”。