2012-09-24 10 views
8

我有一个C++类,它是日志系统的前端。它的日志功能使用C++ 11的可变参数模板来实现:如何在C++ 11可变参数模板中使用GCC的printf格式属性?

template <typename... Args> 
void Frontend::log(const char *fmt, Args&&... args) { 
    backend->true_log(fmt, std::forward<Args>(args)...); 
} 

每个日志后端实现自己的true_log版本,即,除其他事项外,采用转发参数来调用vsnprintf。例如:

void Backend::true_log(const char *fmt, ...) { 
    // other stuff.. 
    va_list ap; 
    va_start(ap, fmt); 
    vsnprintf(buffer, buffer_length, fmt, ap); 
    va_end(ap); 
    // other stuff.. 
} 

一切都很好,我很高兴。

现在,我想为log()参数添加一个静态检查:具体来说,我想使用GCC的printf格式属性。

我开始用__attribute__ ((format (printf, 2, 3)))标记log()函数(因为this是第一个“隐藏”参数,我需要将参数索引移动一个)。这是不行的,如果失败,编译错误,因为:

error: args to be formatted is not ‘...’ 

然后,我想同样的属性添加到true_log()功能。它编译,但没有实际执行错误检查:我试图传递给log()一些无效的格式/变量组合,并且没有发出警告。也许这种检查“太迟了”,换句话说,有关变量的信息在调用链中已经丢失了?

作为最后的手段,如果我注释log()__attribute__ ((format (printf, 2, 0))),我会收到有关错误格式字符串的警告,但不会针对无效的格式/变量组合发出诊断。

总结问题:如果我使用C++ 11的可变参数模板,如何从GCC进行全格式检查?

+0

由于vsnprintf()不能处理比​​什么老学校...可以做什么更多,为什么要首先打扰可变参数模板呢? –

+1

为什么当你丢弃类型信息时使用可变参数模板?只要让'true_log()'成为你真正的日志记录功能。 –

+0

或者让'Frontend :: log'带一个变量参数... –

回答

2

我不相信你可以。我敢打赌,GCC只验证格式字符串,如果它是文字。这就是为什么将format属性放在true_log上不起作用的原因 - 该函数的调用方式与运行时确定的字符串相似(语法上)。直接将它放在log上就可以规避这种情况,但是需要format属性才能支持variadic模板,而您证明它没有。

我建议你看看更多的C++ - ish方法来做格式化输出。例如,boost::format其工作方式与printf类似,但动态验证参数类型的数量和类型与格式字符串匹配。不过,它并没有使用可变参数模板,而是逐个消耗了输入给它的参数(通过运算符%)。

1

为了记录,我最终删除了C++ 11 variadic模板,并使用传统的va_list

__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 
} 
相关问题