How can I add a new argument to the existing variable argument list?
In a multi-threaded program I'm writing a custom print function which accepts a variable argument list.
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);
}
}
Inside this function I want to add the current thread id (using pthread_self()
) to the message getting printed. How can I do it? Is there a way to add it to the existing va_list?
With a variadic macro:
With a variadic macro you can call the function with an argument prepended or appended:
#define t_printf(format, args...) \
_t_printf(format, thread_id, __VA_ARGS__);
This prepends the thread_id
before other arguments. (Note that on the _t_printf()
function you have to modify the format string too.)
If you do this:
t_printf("some format string", a, b, c);
This will expand do this:
_t_printf("some format string", thread_id, a, b, c);
If t_printf()
is called without other argument that format, you will have a trailing comma. GCC has a ##
extension that takes care of adding the comma as needed:
#define t_printf(format, args...) \
_t_printf(format, thread_id ##__VA_ARGS__);
Complete solution with the macro:
#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);
}
}
Without modifying the arguments
An other solution is to do two printf()s:
vsnprintf(buffer, bufsize, str, ap);
vfprintf(file_ptr, "[thread_id: %x] %s", thread_id, buffer);
Complete solution:
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);
}
}
I believe there's no standard way to manipulate a va_list. The stdarg.h header defines macros to declare, initialize, copy, terminate lists and fetch an argument (discerning type is up to caller).
Here's the suggestion of an alternative to accomplish the same result: Note that this imposes a limitation on the format of the string. Depending on what you want, it may not be a problem:
#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);
}
}
Keep it simple. Pass a modified format string to 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);
}
}
First off, I don't think there's a supported way to print the value of pthread_self
(ref: the existence of pthread_equal
), here I'm casting it to a void*
and using "%p" - change as you see fit.
Since, unfortunately, you can't add to a va_list
I'd normally do something like this:
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);
}
}
Since you ruled that out in the comments I assume this is to keep the function general and only have selected programs print thread id. I give you the following monstrosity:
#define t_printf(format, ...) t_printf("%p: " format, (void*)pthread_self(), __VA_ARGS__)
It only works if the format string is a literal and I don't recommend using it in production code, but it's an easy "hack" to get the job done.
精彩评论