[C] Умный print для C
Автор
Сообщение
news_bot ®
Стаж: 6 лет 11 месяцев
Сообщений: 27286
Пример использования:
#include "print.h"
int main() {
print("number:", 25,
"fractional number:", 1.2345,
"expression:", (2.0 + 5) / 3
);
}
number: 25 fractional number: 1.2345 expression: 2.33333
Дженерик вызов не только проще набирать, чем стандартный printf(), но и больше не будет предупреждений компилятора, о том, что символ формата после "%" неверного типа. Дженерик print, может выводить все основные типы языка Си, целые, со знаком и без, с плавающей точкой и указатели:
char *s = "abc";
void *p = main;
long l = 1234567890123456789;
unsigned char byte = 222;
char ch = 'A';
print("string:", s, "pointer:", p, "long:", l);
print(byte, ch)
string: "abc" pointer: 0x402330 long: 1234567890123456789
222<0xDE> 'A'65
Разные типы отображаются разным цветом, палитру можно настроить, либо вообще отключить цвет. Можно даже печатать массивы:
int x[] = { 1, 2, 3 };
char *args[] = { "gcc", "hello.c", "-o", "hello" };
print(x, args);
[1 2 3] ["gcc" "hello.c" "-o" "hello"]
Как это работает? На самом деле print это макрос, точнее говоря variadic macro, который генерирует вызов настоящей функции. Первый параметр, который макрос конструирует для этой функции, это количество аргументов, введённых пользователем. Для этого используется известный трюк:
void __print_func(int count, ...);
#define count_arg(q,w,e,r,t,y,...) y
#define print(a...) __print_func(count_arg(a,5,4,3,2,1,0), a);
Элегантностью такое решение не блещет, спасибо ограничениям препроцессора, как видите, максимальное количество аргументов в этом примере 6, (в моей библиотеке сейчас 26 ).Второй параметр spread operator ... , это сам список всех аргументов. В функции __print_func()используется обычный stdarg.h для обхода этого списка:
void prn(int count, ...) {
va_list v;
va_start(v, types);
for (int i = 0; i < count; i++) {
...
printf("%'li", va_arg(v, unsigned long));
...
}
va_end(v);
}
Теперь, сложный вопрос: как узнать типы? Ведь va_argне волшебник, мы ему джолжны указать тип для каждого аргумента. В примере выше -- это unsigned long, но, что на самом деле пользователь передаст, мы ещё не знаем.Большинство компиляторов Си понимает такую вещь:
int x;
int y = __builtin_types_compatible_p(typeof(x), int);
Это конструкция времени компиляции, принимает типы, а возвращает булевое значение, в данном примере y будет равен 1 или true потому что int == int.Ещё, есть такой вызов, как __builtin_choose_expr(a, b, с). Это аналог a ? b : c времени компиляции, с помощью этих расширений компилятора можно написать, что-то наподобие свитча, который возвращает тип переменной в виде числа, 3 для int, 2 для double и т.д.:
#define __get_type(x) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), double), 1, \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char), 2, \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int), 3, \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), void*), 4, \
....... и так далее
Далее, применяя стандартные трюки с variadic macro, то есть, пишем __get_type(), много или, точнее, count раз через запятую, создаём массив char types[], и подставляем его вторым параметром в вызов функции печати, её заголовок станет таким:
void __print_func (int count, char types[], ...) {
Теперь мы можем смело брать аргументы с помощью va_arg, подглядывая их типы из массива:
for (int i = 0; i < count; i++) {
if (types[i] == 'd') {
double d = va_arg(v, double);
printf("%'G", d);
}
else if (types[i] == 'i') {
double d = va_arg(v, int);
printf("%'i", d);
}
...
}
На самом деле, чтобы печатать массивы, надо ещё пердавать sizeof(), что выглядит, примерно, так:
(short[])(sizeof(a), sizeof(b), sizeof(c),.........)
Для экономии тип и размер упаковываются в unsigned short: __get_type(x) + sizeof(x) << 5.Вся работа препроцессора и builtins компилируется очень эффективно, вот такой вызов:
print(42, 42);
Компилируется gcc -O1в такой код:
xor eax, eax
mov ecx, 42
mov edx, 42
lea rsi, [rsp+12]
mov edi, 2
mov DWORD PTR [rsp+12], 0x00840084
call __print_func
Выше описаные расширения поддерживают компиляторы GCC 5.1+, Clang3.4.+1, Intel C 17.0.0+, и TinyC. На MSVC их нет, возможно, есть похожие, но мне не удалось найти соответствующей информации.Вот как рисуется цвет:
void __print_color(int a) {
if (!__print_enable_color) return;
if (a == -1) printf("\x1b(B\x1b[m");
else printf("\x1b[38;5;%im", a);
}
Поменяв значение глобальной переменной __print_enable_color на 0можно отключить цветной вывод. А функция __print_setup_colors() позволяет задать палитру:
void __print_setup_colors(int normal, int number, int string, int hex, int fractional) {
Надо будет ещё добавить автоматическое отключение цвета если stdout не консоль, а файл или pipe.Есть fprint(fd...) для работы с stderr и любыми дескрипторами.Возможно, у вас вопрос, почему не _Generic, а __builtin_types_compatible_p? Дело в том, что _Generic не отличает массивы от указателей, например int* для него то же самое, что и int[]поэтому с _Generic выводить массивы бы не получилось.Ссылка на github: https://github.com/exebook/generic-print
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка мобильных приложений] Как выбрать мобильную кросс-платформу в 2021 году (перевод)
- [Старое железо] Compaq Contura 410C: неигровое ретро
- [Научно-популярное, Звук] «Подкаст» с марсохода: новые записи красной планеты
- [.NET, C#] Кодогенерацию с использованием Roslyn можно использовать и без перехода на .Net 5 (перевод)
- [Компьютерное железо, Видеокарты, IT-компании] Nvidia подтвердила, что майнинговые видеокарты CMP 30HX и 40HX базируются на старой архитектуре Turing
- [Законодательство в IT, Социальные сети и сообщества] Австралия приняла закон о плате поисковиков за индексируемые новости
- [Open source, Git, Agile, DevOps] Bебинар — Автоматизация процессов с GitLab CI/CD
- [Информационная безопасность, Машинное обучение] Chatbox on Top of SIEM Solution
- Red Hat Enterprise Linux стал бесплатен для организаций, развивающих открытое ПО
- [Java] Использование Google Protocol Buffers (protobuf) в Java (перевод)
Теги для поиска: #_c, #_printf, #_c
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 03-Фев 20:51
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 11 месяцев |
|
Пример использования: #include "print.h"
int main() { print("number:", 25, "fractional number:", 1.2345, "expression:", (2.0 + 5) / 3 ); } number: 25 fractional number: 1.2345 expression: 2.33333
char *s = "abc";
void *p = main; long l = 1234567890123456789; unsigned char byte = 222; char ch = 'A'; print("string:", s, "pointer:", p, "long:", l); print(byte, ch) string: "abc" pointer: 0x402330 long: 1234567890123456789
222<0xDE> 'A'65 int x[] = { 1, 2, 3 };
char *args[] = { "gcc", "hello.c", "-o", "hello" }; print(x, args); [1 2 3] ["gcc" "hello.c" "-o" "hello"]
void __print_func(int count, ...);
#define count_arg(q,w,e,r,t,y,...) y #define print(a...) __print_func(count_arg(a,5,4,3,2,1,0), a); void prn(int count, ...) {
va_list v; va_start(v, types); for (int i = 0; i < count; i++) { ... printf("%'li", va_arg(v, unsigned long)); ... } va_end(v); } int x;
int y = __builtin_types_compatible_p(typeof(x), int); #define __get_type(x) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), double), 1, \ __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char), 2, \ __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int), 3, \ __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), void*), 4, \ ....... и так далее void __print_func (int count, char types[], ...) {
for (int i = 0; i < count; i++) {
if (types[i] == 'd') { double d = va_arg(v, double); printf("%'G", d); } else if (types[i] == 'i') { double d = va_arg(v, int); printf("%'i", d); } ... } (short[])(sizeof(a), sizeof(b), sizeof(c),.........)
print(42, 42);
xor eax, eax
mov ecx, 42 mov edx, 42 lea rsi, [rsp+12] mov edi, 2 mov DWORD PTR [rsp+12], 0x00840084 call __print_func void __print_color(int a) {
if (!__print_enable_color) return; if (a == -1) printf("\x1b(B\x1b[m"); else printf("\x1b[38;5;%im", a); } void __print_setup_colors(int normal, int number, int string, int hex, int fractional) {
=========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 03-Фев 20:51
Часовой пояс: UTC + 5