变量函数:提取参数并追加字符串

时间:2013-12-10 18:23:10

标签: c variadic-functions

我有一个函数log_info(从printf的实现中复制)接受变量号。参数并将其传递给vprintf:

int log_info(const char *format, ...) {
  va_list arg;
  int done;

  va_start (arg, format);
  done = vfprintf (stdout, format, arg);
  va_end (arg);

  return done;
}

我想在这些参数前面加上字符串并附加字符串。因此,如果用户以这种方式调用上述函数:

log_info("at iteration %d, float value is %f", i, f);

而不是打印

at iteration 4, float value is 102.34

我想打印

[INFO] [at iteration 4, float value is 102.34] [timestamp: xxxx]

我可以在3个单独的步骤中完成它

fprintf(stdout, "[INFO] [");
vprintf(stdout, format, arg);
fprintf(stdout, "] [timestamp:%f]", ts);

但该程序是多线程的,因此我希望所有数据都在一次调用vprintf(vprintf是线程安全的)中编写。

另一种选择是锁定一个互斥锁,然后按照上面的步骤分三步编写它,但是如果将字符串附加到args并不太复杂,我想尝试一下。

编辑:由于使用互斥锁而导致的性能开销并不是真正的问题,但除非必要,否则我不想使用它。

提前致谢

2 个答案:

答案 0 :(得分:5)

您想要在输出中添加新字段,因此只需构造一个新的格式字符串。

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

int loginfo(const char *format, ...) {
    int ret;
    int len;
    va_list ap;
    char *new_fmt;
    char *timestamp;
    const char fmt_template[] = "[INFO] [%s] [timestamp: %s]";

    /* Grab the timestamp now, since if we call it twice, it may change in length */
    timestamp = get_time_string();

    /* Calculate length for the augmented format string and allocate. */
    len = snprintf(NULL, 0, fmt_template, format, timestamp);
    new_fmt = malloc(len + 1);

    /* Construct the new format string */
    snprintf(new_fmt, len + 1, fmt_template, format, timestamp);

    /* Print as before, using new format string */
    va_start (ap, format);
    ret = vfprintf (stdout, new_fmt, ap);
    va_end (ap);

    free(new_fmt);
    return ret;
}

答案 1 :(得分:3)

一种方法是使用vsnprintf将原始消息写入(动态分配的)字符串。然后使用fprintf输出元数据和原始消息:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
...

static const char * get_timestamp_str()
{
    ...
}

static void Log(const char *format, ...)
{
    va_list arg;
    int len;
    char * orig_msg;

    /* Compute length of original message */
    va_start(arg, format);
    len = vsnprintf(NULL, 0, format, arg);
    va_end(arg);

    /* Allocate space for original message */
    orig_msg = (char *)calloc(len+1, sizeof(char));

    /* Write original message to string */
    va_start(arg, format);
    vsnprintf(orig_msg, len+1, format, arg);
    va_end(arg);

    /* Write metadata plus original message to stderr */
    fprintf(stderr, "[INFO] [timestamp %s] %s\n", get_timestamp_str(), orig_msg);
    free(orig_msg);
}