__attribute __ ((format (printf, 1, 2))) для MSVC?
С GCC я могу указать __attribute__((format(printf, 1, 2)))
, сообщая компилятору, что эта функция принимает параметры vararg, которые являются спецификаторами формата printf.
Это очень полезно в случаях, когда я обертываю, например. семейство функций vsprintf. я могу иметь
extern void log_error(const char *format, ...) __attribute__((format(printf, 1, 2)));
И всякий раз, когда я вызываю эту функцию, gcc проверяет, соответствуют ли типы и количество аргументов заданным спецификаторам формата, как это было бы для printf, и выдавать предупреждение, если нет.
У компилятора Microsoft C/С++ есть что-то подобное?
Ответы
Ответ 1
В то время как GCC проверяет спецификаторы формата, когда -Wformat включен, VС++ не имеет такой проверки даже для стандартных функций, поэтому нет эквивалента этому __attribute__
, потому что нет эквивалента -Wformat.
Я думаю, что акцент Microsoft на С++ (подтвержденный поддержкой соответствия ISO для С++ при поддержке C89) может быть отчасти причиной того, что VС++ не имеет проверки спецификатора формата; в С++ с использованием спецификаторов формата <iostream>
нет необходимости.
Ответ 2
Используя SAL Аннотации, вы можете использовать _Printf_format_string_
(как VS2k8 или VS2k10), так и __format_string
(для 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), ...);
Ответ 3
Как упоминалось ранее, проверка формата @RustyX printf
теперь поддерживается по умолчанию с VC2015. Это без прохождения статического анализа /analyze
. К сожалению, пока еще нет механизма для маркировки пользовательских функций-оберток.
Это говорит о очевидном обходном пути вызова printf. Это определение макроса, который вызывает как пользовательскую функцию, так и сам printf
. Последний на мертвой траектории для оптимизации.
Это имеет дополнительное преимущество для достижения некоторого уровня переносимости для других компиляторов.
int printf_wrapper_(const char *format, ...);
#define printf_wrapper(...) \
(printf || printf(__VA_ARGS__), printf_wrapper_(__VA_ARGS__))
Недостатком является то, что VC2015 выполняет некоторую элементарную ликвидацию мертвого кода перед проверкой формата, проверяя только оставшийся код в реальном времени.
Таким образом, sizeof
или постоянные условные выражения потерпят неудачу. Как правило, если отладочная сборка испускает код во время выполнения, вы получите предупреждение, хотя позже пропуски в сборках релизов могут по-прежнему убивать вызов.
Увы, это делает его движущей целью, которая может измениться в будущих версиях компилятора. Хотя и относительно мягкий.
Ответ 4
В Code Code есть интересная статья:
"Использование шаблонов С++ для проверки запуска"
Александр Горобец
http://www.codeproject.com/KB/cpp/ValidateprintfFunction.aspx
Я изменил его так, что у меня есть макрос PRINTF_VALIDATE(format, ...)
, который регистрирует все ошибки формата в программе statup (нет необходимости выполнять код). Он производит что-то вроде этого:
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)
Можно использовать его, например, следующим образом:
#define LOG(fmt, ...) do { PRINTF_VALIDATE(fmt, __VA_ARGS__); WriteLog(fmt, __VA_ARGS__); } while(0)
Это не так полезно, как поддержка компилятора, но работает на Visual Studio 2005...