snprintf:相同的代码-在不同的g++编译器上有不同的错误/警告

snprintf: Same code - different errors/warnings on different g++ compilers

本文关键字:错误 警告 编译器 代码 snprintf g++      更新时间:2023-10-16

我知道这个问题以前可能被问过,但还没有回答我在下面发布的查询。我正试图弄清楚为什么下面的代码会出现错误(在GCC的其他版本中,这是一个警告),尤其是如何解决这个问题?我是一名经验丰富的C++程序员,但有时你确实需要寻求帮助。感谢您的帮助。

[错误]:无法通过传递非普通可复制类型"class std::basic_string"的对象

/// USE THE MACRO BELOW TO LOG A FORMATTED STRING (usage is exactly like printf(...) [ SEE HEADER FILE - TEMPLATE FUNCTION user_log_fmt(...) ]
#define LOG_TRACE_FMT(...)  Logger::getInstance()->user_log_fmt(LOG_LEVEL_TRACE,   __PRETTY_FUNCTION__, __FUNCTION__, __VA_ARGS__)
void main()
{   
std::string linkName = getLinkName();
// THIS IS THE CALL THAT LEADS TO THE ERROR AT COMPILE-TIME
LOG_TRACE_FMT("nLink-name: %s, Sent packet: SYS: %d, COMP: %d, LEN: %d, MSG ID: %dn", linkName, msg->sysid, msg->compid, msg->len, msg->msgid);
}
///
/// Part of logger class (combines strings with argument for formatting) 
///
template <typename ... Args>
void user_log_fmt(LOG_LEVEL level,  std::string pretty_func, std::string func_name, std::string str_format, Args ... args)
{
if (m_LogLevel < level)
return;
std::string formatted_data = utils::Utils::string_format(str_format, args...);
// Do something with the formatted string.. 
}
///
/// Part of Utils library. This method does a printf type formatting.
///
template<typename ... Args>
static std::string string_format( const std::string& format, Args ... args )
{
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for ''
std::unique_ptr<char[]> buf( new char[ size ] );
snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '' inside
}

错误出现在上面的snprintf语句中。

g++上的警告(Ubuntu 5.4.0-6ubuntu1~16.04.4)5.4.0 20160609:

警告:format不是字符串文字,也没有格式参数[-Wformat security]size_t size=snprintf(nullptr,0,format.c_str(),args…)+1、 '^

g++错误(Raspbian 4.9.2-10)4.9.2

错误:无法通过"…"传递非普通可复制类型"class std::basic_string"的对象size_t size=snprintf(nullptr,0,format.c_str(),args…)+1.

注意:我希望理解并解决这个问题,但不提供编译器标志设置。我知道这可能与C有关与C++的兼容性。

谢谢!

我找到了这个歧义的答案。感谢@PaulMcKenzie的指出。这是我看到的适用于任何编译器标志(如"-Wno格式安全性")的解决方案。没有更多的错误在Pi或错误在Ubuntu g++。

void main()
{   
std::string linkName = getLinkName();
// THIS IS THE CALL THAT LEADS TO THE ERROR AT COMPILE-TIME
LOG_TRACE_FMT("nLink-name: %s, Sent packet: SYS: %d, COMP: %d, LEN: %d, MSG ID: %dn", linkName.c_str(), msg->sysid, msg->compid, msg->len, msg->msgid);
}

catch正在执行linkName.c_str()(c样式字符串),而不是将std::string作为参数传递。这很棘手!

我不知道是什么导致了您的问题,但我看不出使用模板函数会给您带来什么好处。你可以使用好的老式varargs。

我想说的是,编写varargs函数的黄金法则是编写一个需要va_list的函数。您的编辑非常清楚地说明了原因——您通常最终会从另一个varargs函数调用varag函数!

#include <stdarg.h>  // varargs support
static std::string string_vformat( const std::string& format, va_list args)
{
va_list args_copy;
va_copy(args,args_copy);  // Copy args before we use it.
// +1 is extra space for ''
const size_t size = vsnprintf( nullptr, 0, format.c_str(), args_copy ) + 1; 
va_end(args_copy);        // Release args_copy.
std::unique_ptr<char[]> buf( new char[ size ] );
vsnprintf( buf.get(), size, format.c_str(), args );
// Caller must va_end(args);
// We don't want the '' inside
return std::string( buf.get(), buf.get() + size - 1 );
}
static std::string string_format( const std::string& format, ... )
{
va_list args;
va_start(format, args);
const auto result = string_vformat(format, args);
va_end(args);
return result;
}
void user_log_fmt(LOG_LEVEL level,  std::string pretty_func, std::string func_name,
std::string str_format, ... )
{
if (m_LogLevel < level)
return;
va_list args;
va_start(format, args);
std::string formatted_data = utils::Utils::string_vformat(str_format, args);
// Do something with the formatted string.. 
}

几个旁白:

  • 我会为''分配一个有空间的可写字符串,并为此写入缓冲区,然后缩小缓冲区以丢失终止符。字符串是连续的,因此这是安全的,并且保存动态分配会使最终结果中额外字节的成本非常值得
  • 我会将user_log_fmt的所有参数作为对std::string的常量引用(但这只是因为传递值总是让我抽搐)

最后,当您似乎在使用GCC时,您可以写:

#ifdef __GNUC__
#define PRINTFLIKE(n)  __attribute__(format,printf,(n),(n+1))
#else
#define PRINTFLIKE(n)
#endif

然后你可以申报:

void user_log_fmt(LOG_LEVEL level, std::string pretty_func, std::string func_name,
std::string str_format, ... ) PRINTFLIKE(4)

std::string string_format( const std::string& format, ... ) PRINTFLIKE(1)

如果你做了这样的事情,GCC会给你一个很好的错误:

string_format( "Two things %d %d", only_one_thing );

(如果格式是错误的类型,它也会抱怨。