Подтвердить что ты не робот

__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/С++ есть что-то подобное?

4b9b3361

Ответ 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...