Как использовать предупреждения спецификации типа формата [*]printf?

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

Вы спрашиваете конкретно о GCC, но см. также stackoverflow.com/questions/2354784/… для MSVC

Dúthomhas 27.08.2024 02:35

@KenP, кстати, я бы рекомендовал vsnprintf() вместо vprintf(), учитывая неуправляемые возможности zzzprintf().

chux - Reinstate Monica 27.08.2024 03:24
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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 27.08.2024 02:43

@o11c: Да, я думал, что это произойдет очень давно, но, честно говоря, в любом случае писать новый код для компиляции с использованием чего-то старше 4.7 на данный момент — это мазохист, поэтому я не стал искать дальше. :-)

ShadowRanger 27.08.2024 03:25

Это небольшое уточнение ответа 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 тоже поддерживает этот атрибут.

Kevin 27.08.2024 06:30

Другие вопросы по теме