Я не знаю, что доступно в VS 2015 или VS 2017 (полу-случайный поиск по в документации Microsoft не дал никакого освещения).Однако GCC и Clang поддерживают декларативный атрибут функции :
__attribute__((format(printf(,n,m)))
, который может быть преобразован в достаточно переносимый код, например:
#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 NORETURN void err_abort(const char *format, ...) PRINTFLIKE(1,2);
extern NORETURN void err_error(const char *format, ...) PRINTFLIKE(1,2);
…
extern void err_logmsg(FILE *fp, int flags, int estat, const char *format, ...) PRINTFLIKE(4,5);
…
extern void err_remark(const char *format, ...) PRINTFLIKE(1,2);
PRINTFLIKE(n,m)
макрос говорит, что строка формата printf()
является аргументом n
, а фактические аргументы начинаются с m
.Большинство из них похожи на printf()
со строкой формата в качестве первого аргумента и данными после.Функция err_logmsg()
имеет больше параметров управления перед строкой форматирования в аргументе 4, но аргументы формата начинаются с 5, сразу после этого, как в fprintf()
строка форматирования имеет аргумент 2, а аргументы начинаются с аргумента 3.
Было бы целесообразно разработать функцию с аргументами между строкой формата и списком переменных аргументов, например:
extern NORETURN void err_pos_error(const char *format, const char *filename, int lineno, const char *function, ...) PRINTFLIKE(1,5);
, которая может быть вызвана так:
err_pos_error("Failed to open file '%s': %d - %s\n", __FILE__, __LINE__, __func__, filename, errno, strerror(errno));
Мы можем обсудить, является ли это хорошим дизайном (вероятно, было бы лучше поместить аргументы __FILE__
, __LINE__
и __func__
перед строкой форматирования, а не после, по разным причинам), но этовыполнимый дизайн, который демонстрирует непоследовательные числа в макросе PRINTFLIKE
или использование __attribute__((format(printf,n,m)))
.
NORETURN
- это поддержка макросов для определения функций, которые не возвращают:
#if !defined(NORETURN)
#if __STDC_VERSION__ >= 201112L
#define NORETURN _Noreturn
#elif defined(__GNUC__)
#define NORETURN __attribute__((noreturn))
#else
#define NORETURN /* If only */
#endif /* __STDC_VERSION__ || __GNUC__ */
#endif /* NORETURN */
Код, на котором я основываюсь, доступен в моем репозитории SOQ (Вопросы о переполнении стека) на GitHub в виде файлов stderr.c
и stderr.h
в подкаталоге src / libsoq .