我正在制作一个具有类似 printf 功能的日志函数:
log(LOG_LEVEL lvl, const char* fmt, ...)
。
LOG_LEVEL
只是一个枚举(0 很重要,更高则不太重要)。
如果
lvl
高于另一个变量(例如,current_log_level
),我想尽可能少地做并快速返回。但是,我担心不使用 fmt
之后的参数会破坏堆栈,并且如果可能的话,我想避免编写自己的“检查 fmt 并根据需要调用 va_arg()”,以避免与 printf
可能出现的差异
格式,所以我考虑使用 vsnprintf
并让输出字符串的长度尽可能小。
根据文档,对于
vsnprintf
,如果长度设置为0,输出字符串可以为空,但我不确定这是否会使用所有剩余的参数,或者它是否只是停止处理并返回。
想象一下以下内容(我正在使用 C++17,但我认为这也适用于优秀的旧 C):
enum class LOG_LEVEL : unsigned int { ERROR, WARNING };
LOG_LEVEL current_log_level = LOG_LEVEL::WARNING;
void log(LOG_LEVEL lvl, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
if (static_cast<unsigned int>(lvl) <= static_cast<unsigned int>(current_log_level))
{
/* lock a mutex, write to a file and/or whatever */
}
else
{
vsnprintf(nullptr, 0, fmt, args);
}
va_end(args);
}
我觉得
vsnprintf
(和其他 v*printf 函数)应该使用所有参数,即使它被截断,(如果没有,这在许多程序中将是一个很好的安全问题),但在 man 中没有提到页面,我找不到任何信息。也许我的搜索能力不够好。或者也许有更好、更快和/或更简单的方法来做到这一点?
即使输出被截断,vsnprintf() 也会使用所有 va_list 参数吗?
是一个实现细节。结果必须被截断。 如何实现未指定 - 潜在地,
vsnprintf
源代码可以检查输出是否更长,然后停止处理。
我觉得 vsnprintf (和其他 v*printf 函数)应该使用所有参数,即使它截断
我觉得这是一种不合理的感觉。参数存在于堆栈中,它们就在那里。如果它们没有被访问,就没有被访问,什么也不会发生。
当你什么都不想做的时候,就什么也不做。提前返回看起来像是这里使用的一种风格。
void log(LOG_LEVEL lvl, const char* fmt, ...) {
if (static_cast<unsigned int>(lvl) > static_cast<unsigned int>(current_log_level)) {
return;
}
va_list args;
va_start(args, fmt);
/* lock a mutex, write to a file and/or whatever */
va_end(args);
}