Как получить предупреждения и ошибки времени компиляции в стиле printf

Я хотел бы написать такую ​​же процедуру, как printf, но не функционально, но я бы хотел, чтобы подпрограмма имела те же функции проверки компиляции, что и printf.

Например, если у меня есть:

{
   int i;
   std::string s;
   printf("%d %d",i);
   printf("%d",s.c_str());
}

Компилятор жалуется так:

1 cc1plus: warnings being treated as errors
2 In function 'int main()':
3 Line 8: warning: too few arguments for format
4 Line 9: warning: format '%d' expects type 'int', but argument 2 has type 'const char*'

пример кода

Существуют ли специальные функции printf и co, которые компилятор рассматривает по-разному или есть какой-то трюк, чтобы заставить это работать над любой пользовательской функцией? Конкретные компиляторы, которые меня интересуют, - gcc и msvc

Ответ 1

Различные компиляторы могут реализовать эту функцию по-разному. В GCC он реализуется через спецификатор __attribute__ с атрибутом format (читайте об этом здесь). Причина, по которой компилятор выполняет проверку, заключается только в том, что в стандартных файлах заголовков, поставляемых с GCC, функция printf объявляется с помощью __attribute__((format(printf, 1, 2)))

Точно так же вы можете использовать атрибут format, чтобы расширить те же функции проверки формата на свои собственные вариационные функции, которые используют те же спецификаторы формата, что и printf.

Все это будет работать только в том случае, если соглашение о передаче параметров и используемые вами спецификаторы формата совпадают с стандартными функциями printf и scanf. Проверки жестко закодированы в компилятор. Если вы используете другое соглашение для передачи вариационных аргументов, компилятор не поможет вам его проверить.

Ответ 3

printf() и друзья не являются особенными, потому что они принимают переменное количество аргументов: пользовательские функции могут принимать переменное количество аргументов. Они являются особенными, потому что их поведение определяется стандартом, поэтому компилятор знает, какая корреляция должна быть между строкой формата и аргументами, переданными функции.

Фактически, компилятор знает, сколько аргументов передается функции при ее вызове, поэтому он анализирует строку формата и сравнивает ожидаемое число и типы аргументов с тем, какие аргументы фактически переданы функции, и выдает предупреждение, если они не соответствуют.

Если вы используете С++, я бы не стал писать собственные вариационные функции; есть несколько веских причин использовать их в большинстве проектов. Например, если вы делаете форматирование, используйте потоки или библиотеку, например Boost Format. Любая проблема, которая может быть решена с помощью вариационной функции, может быть решена с использованием невариантной функции, и почти во всех случаях результат более изящный, идиоматический и безопасный для типов.

Ответ 4

Фактически printf вообще не имеет встроенной безопасности во время компиляции. Просто случается, что некоторые более свежие компиляторы внедрили специальные проверки, учитывая, что они точно знают, что означает строка формата с точки зрения дополнительных параметров. Когда вы используете ... в качестве параметра, вы говорите, что хотите принять произвольные аргументы и нести полную ответственность за то, чтобы убедиться, что они верны. Компилятор не может проверить их для безопасности count/type.

Вместо того, чтобы пытаться заставить компилятор помочь вам таким образом, попробуйте использовать подход, используемый стандартными потоками. Используйте функцию (или шаблон), которая возвращает ссылку на this, чтобы разрешить цепочку. Затем компилятор сможет сразу сказать вам, когда аргументы не соответствуют ожидаемому/поддерживаемому.

Ответ 5

В то время как кто-то отправил строку mpl:: string в группы boost. Я думаю, что это действительно могло попасть в библиотеку. Если это так, вы можете реализовать что-то вроде этого, предоставив строку шаблона в качестве параметра шаблона (mpl:: string), а затем используя некоторые довольно глубокие навыки метапрограммирования для анализа в нем битов форматирования. Затем вы будете использовать эту информацию, чтобы выбрать реализацию, которая имеет соответствующие аргументы count и типы.

Нет, я не собираюсь делать это за вас: P Это было бы довольно сложно. Однако я считаю, что это возможно.