提问人:mj2 提问时间:10/18/2023 最后编辑:mj2 更新时间:10/18/2023 访问量:105
C++ 中的 printf 包装器
Printf wrapper in C++
问:
我正在尝试构建一个病态函数,它基本上是 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, ...)
如何将参数包从 传递到 ?
我不想每次都“手动”抓取va_list。(恐怕没有别的办法了?log
make_buffer_fmt
你可能会告诉我参数包,va_list不能很好地混合,但我对C++有点陌生,老实说,当它有时编译时,我已经很高兴了。我现在拥有它的方式,我在我需要它的地方得到了va_list,但是,当然,它只是读取一些随机内存,因为va_list不会传递。make_buffer_fmt
我喜欢这三个点,因为它复制了 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)...);
}
你根本不使用,因为这是为了在你丢失所有类型信息后处理 C 样式的可变参数函数参数。在 C++ 中,您不需要首先丢弃该类型信息(或者至少在调用 C 样式函数之前不需要,例如va_list
vsnprintf
)
唯一(潜在的)缺点是所有这些函数都必须在标头中内联定义。由于他们并没有真正做任何似乎值得隐藏的事情,我认为这不是问题。
请注意,在原始版本中,无论如何都不适合使用参数进行调用。这与 Python 中的参数序列不同,它不能扩展回可变长度的参数列表。它只是一个单一的结构。int write_fmt(int fd, char foo, const char* fmt, ...)
va_list
*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 世纪,并且不介意更改格式字符串语法(或动态分配)。
评论
make_buffer_fmt_v(...., va_list)
va_list
make_buffer_fmt
template<typename...Args> int log_info(const char*fmt, Args...args) { return write_fmt(LOG_FILENO, LOG_INFO, fmt, args...); }