snprintf:相同的代码-在不同的g++编译器上有不同的错误/警告
snprintf: Same code - different errors/warnings on different g++ compilers
我知道这个问题以前可能被问过,但还没有回答我在下面发布的查询。我正试图弄清楚为什么下面的代码会出现错误(在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 );
(如果格式是错误的类型,它也会抱怨。
- 我在 C++ 代码中遇到错误警告:控制到达非空函数 [-Wreturn 类型] 的末尾
- G++ 编译器未为未定义的方法生成错误/警告
- 一个语句中没有多个 cout 的错误/警告
- Clang:覆盖之前在命令行上指定的所有警告和错误警告标志
- 放置新位置的错误警告
- Xcode 8.3.2 中的错误警告:文件的非可移植路径 - 指定的路径与磁盘上的文件名不同
- 编译错误:(警告:控制到达非空函数 [-wreturn 类型] 的末尾)
- snprintf:相同的代码-在不同的g++编译器上有不同的错误/警告
- 有没有办法自定义编译错误/警告消息
- 获取错误:警告C4715::并非所有控制路径都返回值,但不确定原因
- 导致.exe错误警告
- 如何在 SysntasticCheck for vim 中显示所有" g++ -Wall "错误警告?
- 开发基本的C++代码+检查错误/警告
- 如何使g++为int i=i生成错误/警告
- 我得到以下错误:[警告]多字符字符常量
- C++错误:警告:多字符字符常量/a用于用法
- C++ 在开关中隐式跌倒时强制编译时错误/警告
- 打印矩阵时,C ++程序停止,没有错误警告
- Xcode typedef 的 struct 创建错误/警告
- 主函数给出错误警告