如何使用GCC';使用C++;11可变模板? 我有一个C++类,它是日志系统的前端。其日志记录功能使用C++11的可变模板实现: template <typename... Args> void Frontend::log(const char *fmt, Args&&... args) { backend->true_log(fmt, std::forward<Args>(args)...); }
一切都很顺利,我很高兴 现在,我想在如何使用GCC';使用C++;11可变模板? 我有一个C++类,它是日志系统的前端。其日志记录功能使用C++11的可变模板实现: template <typename... Args> void Frontend::log(const char *fmt, Args&&... args) { backend->true_log(fmt, std::forward<Args>(args)...); },c++,gcc,c++11,variadic-templates,function-attributes,C++,Gcc,C++11,Variadic Templates,Function Attributes,一切都很顺利,我很高兴 现在,我想在log()参数上添加一个静态检查:具体来说,我想使用GCC的printformat属性 我首先用\uuuu属性((格式(printf,2,3))标记log()函数(因为这是第一个“隐藏”参数,我需要将参数索引移动一个)。这不起作用,因为如果因编译错误而失败: 错误:要格式化的参数不是“…” 然后,我尝试将相同的属性添加到true\u log()函数中。它可以编译,但实际上没有执行错误检查:我试图传递到log()一些无效的格式/变量组合,但没有发出警告。也许这
log()
参数上添加一个静态检查:具体来说,我想使用GCC的printformat属性
我首先用\uuuu属性((格式(printf,2,3))
标记log()
函数(因为这是第一个“隐藏”参数,我需要将参数索引移动一个)。这不起作用,因为如果因编译错误而失败:
错误:要格式化的参数不是“…”
然后,我尝试将相同的属性添加到true\u log()
函数中。它可以编译,但实际上没有执行错误检查:我试图传递到log()
一些无效的格式/变量组合,但没有发出警告。也许这种检查“太晚了”,或者,换句话说,有关变量的信息在调用链中丢失了
最后,如果我用\uuuuu属性((格式(printf,2,0))
注释log()
,我将收到关于错误格式字符串的警告,但不会对无效格式/变量组合发出诊断
总结问题:如果我使用C++11的可变模板,如何从GCC进行完整格式检查?我不相信您可以。我打赌GCC只会验证文本格式字符串。这就是为什么将format
属性放在true\u log
上不起作用的原因-调用该函数的时候(语法上)看起来像是运行时确定的字符串。直接将其放在日志上可以避免这种情况,但需要格式属性来支持可变模板,而您已经证明它不支持这种情况
我建议您研究更多的C++风格的格式化输出方法。例如,有一个类似printf的boost::format
,但它会动态验证参数类型的数量和类型是否与格式字符串匹配。不过,它不使用可变模板,而是一个接一个地使用(通过运算符%)提供给它的参数。作为记录,我最终删除了C++11可变模板,并使用了传统的va_列表
__attribute__((format(printf, 2, 3)))
void Frontend::log(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
backend->true_log(fmt, ap);
va_end(ap);
}
void Backend::true_log(const char *fmt, va_list ap) {
// log the message somehow
}
如果您愿意使用宏,则有一种变通方法
有些构造将导致编译器为您执行检查,但不会生成任何调用的代码。一个这样的结构是sizeof
。因此,您可以使用宏让记录器直接将参数传递给printf
,但要在sizeof
计算的上下文中,然后调用记录器本身
使用宏的原因是确保格式字符串的处理方式与字符串文字的处理方式相同
在下图中,我将sizeof
计算视为一次性参数,但应该有其他方法应用相同的技术
template <typename... Ts>
void Frontend::log(size_t, const char *fmt, Ts&&... args) {
backend->true_log(fmt, std::forward<Ts>(args)...);
}
#define log(...) log(sizeof(printf(__VA_ARGS__)), __VA_ARGS__)
模板
void Frontend::log(大小、常量字符*fmt、Ts&…参数){
后端->真实日志(fmt,std::forward(args)…);
}
#定义日志(…)日志(sizeof(printf(uu VA_ARGS_uu)),u VA_ARGS_u)
当然,这是一个解决办法。不使用宏的原因有很多。在这种情况下,log
宏将干扰任何其他同名函数或方法。因为vsnprintf()不能处理比老式方法更多的函数或方法。。。我能做的就是,为什么一开始就要麻烦使用可变模板呢?当你扔掉类型信息的时候,为什么还要使用可变模板呢?只需将true\u log()
设置为您真正的日志函数。或者将Frontend::log
设置为变量参数……请注意,转发uu VA_uargs_uuu
您应该使用#u VA_uargs_uu
,而不是我真正喜欢的解决方案。此外,您可能还应该将宏更改为类似这样的内容:#定义日志(f,…)true_log(sizeof(printf(f,####(VA#)(ARGS)),f,##(VA)(ARGS))
@SebastianCabot:@SebastianCabot
template <typename... Ts>
void Frontend::log(size_t, const char *fmt, Ts&&... args) {
backend->true_log(fmt, std::forward<Ts>(args)...);
}
#define log(...) log(sizeof(printf(__VA_ARGS__)), __VA_ARGS__)