Как мы все знаем и любим (или ненавидим), 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