我知道此问题之前可能已被提出,但尚未回答我在下面发布的查询。我试图找出为什么下面的代码确切地给出了错误(以及其他版本的GCC,警告)和esp。怎么解决这个?我是一位经验丰富的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: %d\n", 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 '\0'
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 '\0' inside
}
错误在于上面的snprintf语句。
g ++上的警告(Ubuntu 5.4.0-6ubuntu1~16.04.4)5.4.0 20160609:
警告:格式不是字符串文字而没有格式参数[-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 ++的兼容性。
谢谢!
答案 0 :(得分:1)
我找到了这种含糊不清的答案。谢谢@PaulMcKenzie指出。这是我看到的解决方案,使用任何编译器标志,如“-Wno-format-security”。在Ubuntu g ++上没有错误的Pi或错误。
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: %d\n", linkName.c_str(), msg->sysid, msg->compid, msg->len, msg->msgid);
}
catch正在执行linkName.c_str()(C-Style字符串),而不是将std :: string作为参数传递。这太棘手了!
答案 1 :(得分:1)
我不知道是什么导致了您的问题,但我不知道您使用模板功能会带来什么好处。你可以为此使用古老的varargs。
我要说的是,写一个varargs函数的黄金法则是编写一个va_list
。你的编辑很清楚 - 你通常最终会从另一个varargs函数调用varags函数!
#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 '\0'
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 '\0' 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..
}
一些旁白:
'\0'
分配一个带有空间的可写字符串,然后写入缓冲区,然后缩小缓冲区以丢失终结符。字符串是连续的,因此这是安全的,并且保存动态分配会使最终结果中额外字节的成本非常值得。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 );
(如果格式错误,也会抱怨。