在问了这个问题一年半之后,我提出了一种完全不同的方法来解决真正的问题:有没有办法静态检查自定义可变参数格式化语句的类型?
为了完整性并且因为它可以帮助其他人,这是我最终实施的解决方案。与原始问题相比,它有两个优点:
- 比较简单:不到一天就实现了;
- 独立于编译器:可以在任何平台(Windows、Android、OSX、...)上检查 C++ 代码。
Perl 脚本解析源代码,查找格式化字符串并解码其中的百分比修饰符。然后,它通过调用模板标识函数来包装所有参数CheckFormat<>
。例子:
str->appendFormat("%hhu items (%.2f %%) from %S processed",
nbItems,
nbItems * 100. / totalItems,
subject);
变成:
str->appendFormat("%hhu items (%.2f %%) from %S processed",
CheckFormat<CFL::u, CFM::hh>(nbItems ),
CheckFormat<CFL::f, CFM::_>(nbItems * 100. / totalItems ),
CheckFormat<CFL::S, CFM::_, const BaseString*>(subject ));
枚举CFL
和CFM
模板函数CheckFormat
必须像这样在一个公共头文件中定义(这是一个摘录,大约有 24 个重载)。
enum class CFL
{
c, d, i=d, star=i, u, o=u, x=u, X=u, f, F=f, e=f, E=f, g=f, G=f, p, s, S, P=S, at
};
enum class CFM
{
hh, h, l, z, ll, L=ll, _
};
template<CFL letter, CFM modifier, typename T> inline T CheckFormat(T value) { CFL test= value; (void)test; return value; }
template<> inline const BaseString* CheckFormat<CFL::S, CFM::_, const BaseString*>(const BaseString* value) { return value; }
template<> inline const BaseObject* CheckFormat<CFL::at, CFM::_, const BaseObject*>(const BaseObject* value) { return value; }
template<> inline const char* CheckFormat<CFL::s, CFM::_, const char*>(const char* value) { return value; }
template<> inline const void* CheckFormat<CFL::p, CFM::_, const void*>(const void* value) { return value; }
template<> inline char CheckFormat<CFL::c, CFM::_, char>(char value) { return value; }
template<> inline double CheckFormat<CFL::f, CFM::_, double>(double value) { return value; }
template<> inline float CheckFormat<CFL::f, CFM::_, float>(float value) { return value; }
template<> inline int CheckFormat<CFL::d, CFM::_, int>(int value) { return value; }
...
出现编译错误后,很容易用正则表达式CheckFormat<[^<]*>\((.*?) \)
替换它的捕获来恢复原始表单。