__attribute__((format(printf, 1, 2))) for MSVC?
With GCC, I can specify __attribute__((format(printf, 1, 2)))
, telling the compiler that this function takes vararg parameters that are printf format specifiers.
This is very helpful in the cases where I wrap e.g. the vsprintf function family. I can have
extern void log_error(const char *format, ...) __attribute__((format(printf, 1, 2)));
And whenever I call this function, gcc will check that the types and number of arguments conform to the given format specifiers as it would for printf, and issue a warning if not.
开发者_StackOverflow社区Does the Microsoft C/C++ compiler have anything similar ?
Using SAL Annotations you can use _Printf_format_string_
(as of VS2k8 or VS2k10) or __format_string
(for VS2k5):
#undef FORMAT_STRING
#if _MSC_VER >= 1400
# include <sal.h>
# if _MSC_VER > 1400
# define FORMAT_STRING(p) _Printf_format_string_ p
# else
# define FORMAT_STRING(p) __format_string p
# endif /* FORMAT_STRING */
#else
# define FORMAT_STRING(p) p
#endif /* _MSC_VER */
/* use /analyze or _USE_ATTRIBUTES_FOR_SAL for checking */
extern void log_error(FORMAT_STRING(const char* format), ...);
As previously mentioned by @RustyX printf
format checking is now supported by default as of VC2015. That is without a /analyze
static analysis pass. Regrettably there is not yet a mechanism for marking user-defined wrapper functions.
This suggest the obvious workaround of calling printf. That is defining a macro which invokes both the user-defined function as well as the printf
itself. The latter on a dead path to be optimized out.
This has the added benefit of achieving some level of portability to other compilers.
int printf_wrapper_(const char *format, ...);
#define printf_wrapper(...) \
(printf || printf(__VA_ARGS__), printf_wrapper_(__VA_ARGS__))
The drawback is that VC2015 performs some rudimentary dead-code elimination prior to the format check, testing only the remaining live code.
Thus sizeof
or constant conditional expressions will fail. As a rule of thumb if a debug build emits run-time code then you will get the warning, though later passes in release builds may still kill the call.
Alas this makes it something of a moving target liable to change in future compiler versions. Albeit a relatively benign one.
While GCC checks format specifiers when -Wformat is enabled, VC++ has no such checking, even for standard functions so there is no equivalent to this __attribute__
because there is no equivalent to -Wformat.
I think Microsoft's emphasis on C++ (evidenced by maintaining ISO compliance for C++ while only supporting C89) may be in part the reason why VC++ does not have format specifier checking; in C++ using <iostream>
format specifiers are unnecessary.
There is an interesting article on the subject on Code Project: "Using C++ Templates for Startup Validation" by Alexander Gorobets http://www.codeproject.com/KB/cpp/ValidateprintfFunction.aspx
I've modified it so that I have a macro PRINTF_VALIDATE(format, ...)
that logs all format errors at program statup (there's no need to actually execute the code). It produces something like this:
test.cpp(147) : error : 'printf' format character 'f' at position 1 does not match parameter type INT
test.cpp(147) : error : 'printf' too many arguments (3 instead of 2)
One can use it for example like this:
#define LOG(fmt, ...) do { PRINTF_VALIDATE(fmt, __VA_ARGS__); WriteLog(fmt, __VA_ARGS__); } while(0)
This is not as useful as compiler support, but it works on Visual Studio 2005...
Workaround for MSVS, GCC and clang: "If you’re using a macro to call your printf-like functions, you can use a helper-macro to get compile time format checks like this:
#define CHECK_FORMAT(...) \
do { \
char const dummy = sizeof(printf(__VA_ARGS__)); \
(void)dummy; \
} while (false)
#define MY_FMT(...) \
do { \
CHECK_FORMAT(__VA_ARGS__); \
MyFormatFunc(__FILE__, __LINE__, __VA_ARGS__); \
} while (false)
https://godbolt.org/z/38PaG5fx6
The printf call in the sizeof isn’t evaluated so it doesn’t generate code, but current versions of MSVC, GCC and Clang will still do the format-string check so you get the warning. The local dummy variable is also optimized away with -O2."
Source
精彩评论