C++ 中的 Printf 包装器

问题描述 投票:0回答:1

我正在尝试构建一个病态的函数,它基本上是 printf 的包装器。

我需要使用 vsnprintf 构建一个 char* 缓冲区。这是执行此操作的函数的签名:

char* make_buffer_fmt(char foo, const char* fmt, ...)

现在,当然还有另一个函数围绕这个函数:

int write_fmt(int fd, char foo, const char* fmt, ...)

还有一个环绕

write_fmt
int log(const char* fmt, ...)

如何将参数包从

log
传递到
make_buffer_fmt
? 我不想每次都“手动”获取 va_list 。 (恐怕没有别的办法了吗?)

你可能会告诉我参数包和 va_list 不能很好地混合,但我对 C++ 有点陌生,老实说,当它有时编译时我已经很高兴了。按照我现在的方式,我在需要的地方得到了 va_list,在

make_buffer_fmt
但是,当然它只是读取一些随机内存,因为 va_list 没有被传递。

我喜欢这三个点,因为它复制了 printf 的行为,这是最终的目标。当我将

...
更改为 va_list args 时,它不再需要我的 char* :(.

编辑2:我认为我的问题不太清楚,所以这里有一些代码:

int log_info(const char *fmt, ...) { // Is this how I have to do it? 
    va_list va;                      // Problem I have with this, it that write_fmt doesn't have the '...'
    va_start(va, fmt);               // Instead, I have to pass the va_list to write_fmt, which is not what I want. 
    return write_fmt(LOG_FILENO, LOG_INFO, fmt, va); // Because I want to be able to call write_fmt like this: write_fmt(LOG_FILENO, LOG_INFO, "Hello %s", "World");
    va_end(va);
}

编辑:这是解决方案,但它很丑陋且不切实际,并且有很多样板代码:将变量参数传递给另一个接受变量参数列表的函数

c++ printf wrapper
1个回答
0
投票

如何将参数包从

log
传递到
make_buffer_fmt

首先,您需要一个参数包。

这是一个带有参数包的 C++ 可变参数函数模板:

template <typename... Args>
int log(const char *fmt, Args&&... args)
{
    return write_fmt(LOG_FILENO, LOG_INFO, fmt, std::forward<Args>(args)...);
}

您根本不使用

va_list
,因为这是在丢失所有类型信息后处理 C 风格的可变参数函数参数。在 C++ 中,您不需要首先丢弃该类型信息(或者至少在您调用像
vsnprintf
这样的 C 风格函数之前不需要)

唯一(潜在)的缺点是所有这些函数都必须在标头中内联定义。因为他们并没有真正做任何看起来值得隐藏的事情,所以我认为这不是问题。


请注意,在您的原始版本中,

int write_fmt(int fd, char foo, const char* fmt, ...)
无论如何都不适合使用
va_list
参数进行调用。这与 Python 中的
*args
参数序列不同,它无法扩展回可变长度参数列表。它只是一个结构。

这就是为什么标准库同时具有

snprintf
vsnprintf
:

int snprintf(char* buffer, std::size_t buf_size, const char* format, ...);
int vsnprintf(char* buffer, std::size_t buf_size, const char* format, std::va_list vlist);

无论如何,

write_fmt
应该是与上面类似的完美转发直通,然后
make_buffer_fmt
将使用其扩展参数包调用
snprintf

template <typename... Args>
int make_buffer_fmt(char *buffer, size_t bufsize, const char *fmt, Args&&... args)
{
    return snprintf(buffer, bufsize, fmt,  std::forward<Args>(args)...);
}

vsnprintf
并带有明确的
va_list
(尽管这样做确实没有任何好处)

int make_buffer_fmt(char *buffer, size_t bufsize, const char *fmt, ...)
{
    va_list va;
    va_start(va, fmt);
    int rc = vsnprintf(buffer, bufsize, fmt, va);
    va_end(va);
    return rc;
}

std::format
如果您准备好拥抱 21 世纪,并且不介意更改格式字符串语法(或动态分配)。

© www.soinside.com 2019 - 2024. All rights reserved.