C++ snprintf:相同的代码-不同g++;编译程序
我知道以前可能有人问过这个问题,但没有回答我在下面发布的问题。我试图弄清楚为什么下面的代码会给出错误(在其他版本的GCC中,会给出警告),特别是如何解决这个问题?我是一个经验丰富的C++程序员,但有时你确实需要寻求帮助。感谢您的帮助 [错误]:无法通过传递非普通可复制类型“class std::basic_string”的对象C++ snprintf:相同的代码-不同g++;编译程序,c++,c++11,g++,printf,C++,C++11,G++,Printf,我知道以前可能有人问过这个问题,但没有回答我在下面发布的问题。我试图弄清楚为什么下面的代码会给出错误(在其他版本的GCC中,会给出警告),特别是如何解决这个问题?我是一个经验丰富的C++程序员,但有时你确实需要寻求帮助。感谢您的帮助 [错误]:无法通过传递非普通可复制类型“class std::basic_string”的对象 /// USE THE MACRO BELOW TO LOG A FORMATTED STRING (usage is exactly like printf(...)
/// 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
}
///使用下面的宏记录格式化字符串(用法与printf(…)完全相同[请参阅头文件-模板函数user_LOG_fmt(…)]
#定义日志跟踪FMT(…)记录器::getInstance()->用户日志FMT(日志级别跟踪、函数、函数、参数)
void main()
{
std::string linkName=getLinkName();
//这是导致编译时出错的调用
日志跟踪(“\n链接名称:%s,发送的数据包:SYS:%d,COMP:%d,LEN:%d,MSG ID:%d\n”,链接名称,MSG->sysid,MSG->compid,MSG->LEN,MSG->msgid);
}
///
///logger类的一部分(将字符串与用于格式化的参数组合在一起)
///
模板
无效用户日志fmt(日志级别,标准::字符串函数,标准::字符串函数名称,标准::字符串str格式,Args…Args)
{
如果(m_LogLevel
错误与上面的snprintf语句有关
g++(Ubuntu 5.4.0-6ubuntu1~16.04.4)5.4.0 20160609上的警告:
警告:格式不是字符串文字,也没有格式参数[-Wformat security]
size\u 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: %d\n", linkName.c_str(), msg->sysid, msg->compid, msg->len, msg->msgid);
}
捕获操作是执行linkName.c_str()(c样式字符串),而不是将std::string作为参数传递。这很棘手
我找到了这个模糊性的答案。感谢@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: %d\n", linkName.c_str(), msg->sysid, msg->compid, msg->len, msg->msgid);
}
捕获操作是执行linkName.c_str()(c样式字符串),而不是将std::string作为参数传递。这很棘手
我不知道是什么导致了你的问题,但我不知道你从使用模板函数中得到了什么好处。你可以使用好的老式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..
}
然后你可以声明:
void user_log_fmt(LOG_LEVEL level, std::string pretty_func, std::string func_name,
std::string str_format, ... ) PRINTFLIKE(4)
及
如果您执行以下操作,GCC将向您发出一个很好的错误:
string_format( "Two things %d %d", only_one_thing );
(如果格式类型错误,它也会抱怨。我不知道是什么导致了您的问题,但我看不出使用模板函数有什么好处。您可以使用好的老式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..
}
然后你可以声明:
void user_log_fmt(LOG_LEVEL level, std::string pretty_func, std::string func_name,
std::string str_format, ... ) PRINTFLIKE(4)
及
如果您执行以下操作,GCC将向您发出一个很好的错误:
string_format( "Two things %d %d", only_one_thing );
(如果格式错误,也会抱怨。@ SoavavHOH谢谢。),SNPrTNF与C有关,我把它标记为C。内容存储为C字符串。@萨米,很好,但是IMSO这个问题是基于C++语言来寻找答案的,C和C++是不一样的。它涉及C++与C的可兼容性,但我t与C语言无关-因此标记是不合适的。@Sammy:这些错误是来自模板的声明,还是来自用法?如果是后者,您能否显示调用(以及所有相关的声明)。Ubuntu错误看起来像是在调用
string\u格式(“Hello World”)
。Pi错误看起来像std::string foo=“foo”string_格式(“Hello%s”,foo)“代码/ SNPrTNFF <代码>是C函数,但你传递的东西只不过是C++的。这肯定不能可靠地工作。如果我正确地阅读C++标准的代码> 18.103</代码>,那么传递不符合参数列表的任何东西是不确定的行为。换句话说,你最好做PURLE。yC
或C
兼容工作,如果您正在使用varargs
函数,如snprintf
,printf
,等等。@SouravGhosh感谢您指出。但是snprintf确实与C有关,这是我将其标记为C的原因。内容存储为C-string。@Sammy这很好,但我怎么看这个问题