Как мы все знаем и любим (или ненавидим), GCC имеет возможность выдавать предупреждения при попытке использовать семейство функций printf() с несовпадающими спецификациями типов. В нашем коде имеется ряд служебных функций вида:
int zzzprintf(DataType *dt, const char *format, ...) {
va_list args;
va_start(args, format);
int status = vprintf(dt->buf, format, args);
va_end(args);
return status
}
Мне хотелось бы видеть тот же набор семантики предупреждений вокруг функции zzzprintf(), чтобы если вы, скажем, call:
int64_t id64;
zzzprintf(dt, "Hello, %d\n", id64);
вы получите предупреждение:
/tmp/foo.c: In function ‘main’:
/tmp/foo.c:7:20: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int64_t’ {aka ‘long int’} [-Wformat=]
zzzprintf("Hello %d\n", id64);
~^ ~~
%ld
Примечание. Я не прошу усовершенствовать компилятор GCC. Я ищу способ сообщить компилятору, что он ожидает такого поведения, с помощью #pragma или чего-то подобного. В настоящее время мы используем gcc-8.4.0 и не имеем возможности легко обновить наш gcc, поскольку на данный момент мы привязаны к Ubuntu 18.04.
@KenP, кстати, я бы рекомендовал vsnprintf() вместо vprintf(), учитывая неуправляемые возможности zzzprintf().





GCC поддерживает Атрибуты функций, включая один для имитации проверки типа printf (или scanf, strftime или strfmon), format. Вы можете использовать его, объявив свою функцию в соответствующем заголовке:
int zzzprintf(DataType *dt, const char *format, ...)
__attribute__ ((format (printf, 2, 3)));
что заставляет все исходные файлы, включая этот заголовок, обрабатывать аргументы, используя проверки компилятора, связанные с printf.
Как указывает ссылка вверху, это поддерживается по крайней мере начиная с GCC 4.7.2, поэтому ограничения вашего компилятора не должны создавать проблемы.
Это есть в документации GCC 2.95.3, которая является самой старой на сайте (до этого версии и так странные из-за форка EGCS): gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_4.html #SEC84
@o11c: Да, я думал, что это произойдет очень давно, но, честно говоря, в любом случае писать новый код для компиляции с использованием чего-то старше 4.7 на данный момент — это мазохист, поэтому я не стал искать дальше. :-)
Это небольшое уточнение ответа ShadowRanger:
На некоторых из моих целевых платформ я иногда использую компиляторы, отличные от GCC, поэтому для инкапсуляции функциональности я использую макрос.
#if !defined(PRINTFLIKE)
#if defined(__GNUC__)
#define PRINTFLIKE(n, m) __attribute__((format(printf, n, m)))
#else
#define PRINTFLIKE(n, m) /* If only */
#endif /* __GNUC__ */
#endif /* PRINTFLIKE */
…
extern void err_logmsg(FILE *fp, int flags, int estat, const char *format, ...) PRINTFLIKE(4, 5);
extern void err_print(int flags, int estat, const char *format, va_list args);
extern void err_printversion(const char *program, const char *verinfo);
extern void err_remark(const char *format, ...) PRINTFLIKE(1, 2);
extern void err_report(int flags, int estat, const char *format, ...) PRINTFLIKE(3, 4);
extern void err_sysrem(const char *format, ...) PRINTFLIKE(1, 2);
Поскольку я разрабатываю с помощью GCC, проверка ошибок работает на моих платформах разработки, и я полагаюсь на нее, чтобы защитить себя на других платформах. Жизнь стала проще, так как я больше не работаю ни с какими 32-битными платформами.
Для справки, clang тоже поддерживает этот атрибут.
Вы спрашиваете конкретно о GCC, но см. также stackoverflow.com/questions/2354784/… для MSVC