A year and a half after I asked this question, I had a completely different approach to solving the real problem: is there a way to statically check the types of custom formatting variables?
For completeness and because it can help other people, here is a solution that I finally implemented. It has two advantages over the original question:
- Relatively simple: implemented in less than a day;
- Compiler Independent: You can test C ++ code on any platform (Windows, Android, OSX, ...).
The Perl script parses the source code, finds the format strings, and decodes the percent modifiers inside them. Then it wraps all the arguments with a call to the CheckFormat<> template identification function. Example:
str->appendFormat("%hhu items (%.2f %%) from %S processed", nbItems, nbItems * 100. / totalItems, subject);
becomes:
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 and CheckFormat template CheckFormat must be defined in a common header file like this (this is an extract, there are about 24 overloads).
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; } ...
After compilation errors, it is easy to restore the original form by replacing the regular expression CheckFormat<[^<]*>\((.*?) \) its capture.