我正在尝试构建一个病态的函数,它基本上是 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);
}
编辑:这是解决方案,但它很丑陋且不切实际,并且有很多样板代码:将变量参数传递给另一个接受变量参数列表的函数
如何将参数包从
传递到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 世纪,并且不介意更改格式字符串语法(或动态分配)。