Вывести спецификатор формата из типа данных?

Можно ли программно вывести спецификатор формата для типа данных? Например, если печать длится долго, она автоматически делает что-то вроде:

printf("Vlaue of var is <fmt_spec> ", var);

Я также чувствую, что это уменьшит количество ошибок со стороны разработчика, поскольку что-то вроде

printf("Name is %s",int_val); //Oops, int_val would be treated as an address

printf("Name is %s, DOB is",name,dob); // missed %d for dob

printf("Name is %s DOB is %d", name);//Missed printing DOB

Я понимаю, что у последних двух есть предупреждения, но не было бы лучше, если бы выдавались ошибки, поскольку в большинстве случаев это будет проблематично? Или я что-то упускаю или для этого уже есть конструкции?


person Zoso    schedule 20.06.2017    source источник
comment
Некоторые компиляторы (GCC, Clang) выдают предупреждения о несоответствии форматов и аргументов. Но чтобы ответить на ваш вопрос: нет, это не совсем возможно. C не имеет встроенных возможностей самоанализа или отражение. Нельзя стандартным способом получить типы данных во время выполнения.   -  person Some programmer dude    schedule 20.06.2017
comment
Кроме того, с помощью GCC и Clang вы можете легко превратить определенные предупреждения в ошибки. Или превратите все предупреждения в ошибки, а затем отключите ошибки для определенных предупреждений.   -  person Some programmer dude    schedule 20.06.2017
comment
Спецификаторы формата предназначены не только для типов. Например. %o и %x оба берут unsigned int; %e, %f, %g все берут double; %d, %i, %c все берут int. Вот почему вы не можете (вообще) вывести их из аргументов.   -  person melpomene    schedule 20.06.2017
comment
Поскольку переменные создает программист, ожидается, что он знает тип переменной, и, таким образом, ответственность лежит на вызывающей стороне. Не существует хорошего программного способа работы со спецификаторами формата.   -  person Joshua Detwiler    schedule 20.06.2017
comment
Если бы это было возможно, то зачем С требовало бы делать это вручную?   -  person Eugene Sh.    schedule 20.06.2017
comment
Теоретически это возможно. Я считаю, что это можно сделать с помощью вариативных шаблонов и сканирования строк формата constexpr. Задача не из простых, за сомнительную прибыль, но возможно. Если вы действительно беспокоитесь о правильности типов, вам следует использовать потоки.   -  person Michaël Roy    schedule 20.06.2017
comment
@MichaëlRoy Вопрос о C.   -  person Eugene Sh.    schedule 20.06.2017
comment
У них есть предупреждения. C не имеет никакого кокона, исходящего от ОС и полезных, но не обязательных предупреждений компилятора. Другие языки могут помочь вам больше, но C невероятно эффективен.   -  person Weather Vane    schedule 20.06.2017
comment
gcc -std=c11 -Wall -Wextra -Wconversion -Wpedantic -Werror … и вуаля! Жизнь была бы намного безопаснее, если бы производители огнестрельного оружия не просверливали такие длинные отверстия в стволах. Ой, подождите, тогда эти вещи не имели бы смысла…   -  person too honest for this site    schedule 21.06.2017
comment
@Olaf Хорошая идея, но это будет не предупреждать о printf("%hhd, some_int);, так как спецификаторы печати sub-int/unsigned соответствуют всем int/unsigned.   -  person chux - Reinstate Monica    schedule 21.06.2017
comment
@chux: Да, это был выстрел в темноте. Однако он предупреждает о UB. Если кто-то склонен простреливать себе ноги, он не должен носить ружье. IOW: C — язык не для неосторожных людей. Отсюда и мои придирки. Тем не менее, не следует пытаться сделать C другим языком. Макросы полезны, но если слишком усердствовать с ними, то в конечном итоге вы попадете в ад макросов. Наверное, всем новичкам приходится переходить эту реку - лучшие принимают советы, а посредственные (или те, которые не получают этого совета) делают это один раз.   -  person too honest for this site    schedule 21.06.2017


Ответы (2)


Вывести спецификатор формата из типа данных?

No.

Как сказала Мельпомена:

«Спецификаторы формата предназначены не только для типов. Например, %o и %x оба принимают unsigned int; %e, %f, %g все принимают double; %d, %i, %c все принимают int. Вот почему вы не можете (в общем) вывести их из аргументы».

Суть в том, что если бы такая функциональность существовала, то выводила бы она, например, unsiged int в %o или %x? И так далее . . .


О том, следует ли в некоторых случаях выдавать предупреждение или проблему, следует подумать о том, как работает приведение в c, и когда есть смысл разрешать что-то или нет. В GCC вы, конечно, можете рассматривать предупреждения как ошибки:

-Werror
Make all warnings into errors.

-Werror=
Make the specified warning into an error. The specifier for a warning is appended; for example -Werror=switch turns the warnings controlled by -Wswitch into errors. This switch takes a negative form, to be used to negate -Werror for specific warnings; for example -Wno-error=switch makes -Wswitch warnings not be errors, even when -Werror is in effect.

The warning message for each controllable warning includes the option that controls the warning. That option can then be used with -Werror= and -Wno-error= as described above. (Printing of the option in the warning message can be disabled using the -fno-diagnostics-show-option flag.)

Note that specifying -Werror=foo automatically implies -Wfoo. However, -Wno-error=foo does not imply anything.

как вы можете прочитать здесь.

person gsamaras    schedule 20.06.2017

Можно ли программно вывести спецификатор формата для типа данных?

Не легко и не напрямую с printf(), пока...

Да, с ограничениями на выбранный набор типов, с использованием _Generic.

Это можно было сделать разными способами и использовать с *printf() с большим трудом, но я нашел аналогичный подход к печати данных, без указания индивидуальных спецификаторов формата в этом примере:
Форматированная печать без необходимости указывать спецификаторы соответствия типов с помощью _Generic
Примечание. В этом коде есть дыра в коде, касающаяся математики указателей, которую я с тех пор исправил, но не опубликовал.

GPrintf("Name is ", GP(name), " is ", GP(dob), GP_eol);

Ключевым моментом было использование _Generic(parameter) для управления выбором кода, используемого для преобразования типа в текст, путем расширения макроса GP(x) до 2 частей: строки и x. Затем GPrintf() интерпретирует аргументы.
Это похоже на @Michaël Roy, оставаясь при этом на C, а не на C++.

person chux - Reinstate Monica    schedule 20.06.2017
comment
Имеется отчет о дефекте _Generic. Некоторая интерпретация будет оставлена ​​на реализацию. Это также не будет работать должным образом, например, с. size_t, который представляет собой typedef стандартного типа, но использует отдельный спецификатор типа преобразования. - person too honest for this site; 21.06.2017
comment
@Olaf Относительно size_t, который является typedef, можно обрабатывать с уровнями _Generic. Например. Верхний уровень использует unsigned, unsigned, long, unsigned long long и default. default использует и 2-й уровень Generic, который имеет size_t. Это не всегда обеспечивает z, но позволяет коду различать, важно ли это. IOWs можно использовать спецификатор comparable. - person chux - Reinstate Monica; 21.06.2017
comment
Пожалуйста, прочтите 6.5.1.1p2: … Никакие две общие ассоциации в одном общем выборе не должны указывать совместимые типы … - Вы не согласны с тем, что псевдоним typedef unsigned int size_t; совместим с unsigned int? Прочитать отчет; проблем больше. Он явно указывает clang и gcc как неидентичные реализации. Помимо этого: это слишком много запутывания для несуществующей проблемы. Язык C статически типизирован, все типы известны во время компиляции, и при печати обычно используются известные типы. Специальный вывод требует особого внимания (как показывают связанные примеры). Решение ищет проблему. - person too honest for this site; 21.06.2017
comment
@Olaf Я не предлагал size_t и unsigned в одном и том же общем выборе, но на разных уровнях. Краткий пример: #define info2(X) _Generic((X), \ size_t: "z", \ default: "?" \ ) #define info(X) _Generic((X), \ unsigned long long: "ull", \ unsigned long: "ul", \ unsigned: "u", \ default: info2(X) \ ) int main() { size_t sz; puts(info(sz)); unsigned u; puts(info(u)); } - person chux - Reinstate Monica; 21.06.2017
comment
Хорошо, это может сработать. Но что, если size_t является дополнительным типом компилятора. gcc, например, определяет множество внутренних типов и макросов, чтобы заголовки были более чистыми (например, INT_MIN и т. д. определены таким образом). Честно: вы действительно рекомендуете все это? Если у вас есть только молоток, любая проблема выглядит как гвоздь. Некоторые проблемы просто не решаются молотком. И если есть много выходных данных, динамические языки, такие как Python, безусловно, являются лучшим выбором. - person too honest for this site; 21.06.2017
comment
@Olaf двухуровневая идея наверняка работа с size_t является дополнительным типом компилятора. При необходимости можно добавить уровни. ГТГ - person chux - Reinstate Monica; 21.06.2017
comment
… делая весь ад макросов зависящим от реализации. Разве не этого следует избегать? Слишком причудливо, правда. - person too honest for this site; 21.06.2017