将可变模板参数转发到类似printf的函数

Forwarding variadic template parameter to printf-like function

本文关键字:printf 函数 转发 参数      更新时间:2023-10-16

我有以下日志函数:

template<typename... Arguments>
void Log(const char* file, const int line, int level, const char* fmt, Arguments... args)
{
   std::string formattedFile;
   if (file)
   {
      boost::filesystem::path p(file);
      formattedFile = p.filename().string();
   }
   std::string message{boost::str(boost::format("%1%:%2% [%3%] - %s") % formattedFile % line % m_uxid % fmt)};
   __android_log_print(level, m_tag.c_str(), message.c_str(), args...);
}

该应用程序使用NDK在Android上运行,因此这是该平台的日志记录系统。问题是__android_log_print()无法使用进行编译

error: format not a string literal and no format arguments [-Werror=format-security]
          __android_log_print(level, m_tag.c_str(), message.c_str(), std::forward<Arguments>(args)...);
                                                                                                 ^

我不知道这意味着什么。我是否没有正确使用可变模板参数?

printf中不受信任的输入可能是一个安全问题。使用字符串文字强制格式是提高安全性的一种方法

将警告转换为错误将导致生成失败,因此您必须处理警告。

GCC的警告选项是这样说的

-Werror:
将所有警告转化为错误。

-Wformat-security:
警告使用表示可能存在安全问题的格式函数
目前,这会警告对printf和scanf函数的调用,其中格式字符串不是字符串文字并且没有格式参数
如果格式字符串来自不受信任的输入并且包含%n,则这可能是一个安全漏洞。

通常建议在函数中创建一个std::string,并将其与%s格式的字符串文字一起传递给日志记录函数

__android_log_print(level, m_tag.c_str(), "%s", message.c_str());

其中,消息是通过处理args...构建的,通常使用类似boost::formatstd::stringstream的东西。

如果你想使用你提供的fmt字符串和可变参数,你可以使用一个自定义的printf风格的函数来解析这些参数,这个函数会产生一个std::string

std::string va_string_printf(const char* format, va_list ap)
{
    char stack_buf[256];
    char* buf = stack_buf;
    int buf_size = sizeof(stack_buf);
    std::string out_str;
    while(true)
    {
        va_list ap1;
        va_copy(ap1, ap);
        int min_buf_size = vsnprintf(buf, buf_size, format, ap1) + 1;
        va_end(ap1);
        if (min_buf_size > buf_size)
        {
            if (buf != stack_buf) // allocate a bigger buffer
                delete[] buf;
            buf = new char[min_buf_size];
            buf_size = min_buf_size;
            continue;
        }
        out_str = buf;
        break;
    }
    if (buf != stack_buf)
        delete[] buf;
    return out_str;
}
std::string string_printf(const char* format, ...)
{
    va_list ap;
    va_start(ap, format);
    std::string str = va_string_printf(format, ap);
    va_end(ap);
    return str;
}