LINUX.ORG.RU

Библиотека для форматирования текста в Си

 ,


1

2

Привет, ЛОР!

Скажи, а есть ли что-то типа https://fmt.dev, но на чистом Си? Об printf() я слегка задолбался. Из требований: никаких внешних вызовов (будет работать без ОС), хочу тупо форматирование в строку а-ля snprintf(), но без сраных процентов и угадывания строчки к типу.

Традиционные касты @firkax @Iron_Bug

★★★★★

Последнее исправление: hateyoufeel (всего исправлений: 1)

https://fmt.dev

{fmt} has a small self-contained codebase with the core consisting of just three headers and no external dependencies.

The library is highly portable and requires only a minimal subset of C++11 features which are available in GCC 4.9, Clang 3.4, MSVC 19.10 (2017) and later. Newer compiler and standard library features are used if available, and enable additional functionality.

Where possible, the output of formatting functions is consistent across platforms.

А либка-то прикольная, даже древние версии компиляторов умеет.

EXL ★★★★★
()

В STC 5.0 есть https://github.com/stclib/STC/blob/master/include/c11/fmt.h:

#define i_implement
#include "c11/fmt.h"

int main(void) {
    const double pi = 3.141592653589793;
    const size_t x = 1234567890;
    const char* string = "Hello world";
    const wchar_t* wstr = L"The whole";
    const char z = 'z';
    _Bool flag = 1;
    unsigned char r = 123, g = 214, b = 90, w = 110;
    char buffer[64];

    fmt_print("Color: ({} {} {}), {}\n", r, g, b, flag);
    fmt_println("Wide: {}, {}", wstr, L"wide world");
    fmt_println("{:10} {:10} {:10.2f}", 42ull, 43, pi);
    fmt_println("{:>10} {:>10} {:>10}", z, z, w);
    fmt_printd(stdout, "{:10} {:10} {:10}\n", "Hello", "Mad", "World");
    fmt_printd(stderr, "100%: {:<20} {:.*} {}\n", string, 4, pi, x);
    fmt_printd(buffer, "Precision: {} {:.10} {}", string, pi, x);
    fmt_println("{}", buffer);
    fmt_println("Vector: ({}, {}, {})", 3.2, 3.3, pi);

    fmt_stream ss[1] = {0};
    fmt_printd(ss, "{} {}", "Pi is:", pi);
    fmt_print("{}, len={}, cap={}\n", ss->data, ss->len, ss->cap);
    fmt_printd(ss, "{} {}", ", Pi squared is:", pi*pi);
    fmt_print("{}, len={}, cap={}\n", ss->data, ss->len, ss->cap);
    fmt_close(ss);

    time_t now = time(NULL);
    struct tm t1 = *localtime(&now), t2 = t1;
    t2.tm_hour += 48;
    mktime(&t2);
    fmt_print("Dates: {} and {}\n", fmt_time("%Y-%m-%d %X %Z", &t1, 63),
                                    fmt_time("%Y-%m-%d %X %Z", &t2, 63));
}
dataman ★★★★★
()
Ответ на: комментарий от dataman

Туда проверку формата во время компиляции не добавить, а так же добавление новых типов без патча исходника под каждый их них. Хотя если нужно только «snprintf без угадывания типов» то да.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от dataman
#define fmt_time(fmt, tm, MAXLEN) _fmt_time(fmt, tm, (char[MAXLEN + 1]){0}, MAXLEN + 1)
const char* _fmt_time(const char *fmt, const struct tm* tm, char* buf, int bufsize) {
    strftime(buf, (size_t)bufsize, fmt, tm);
    return buf;
}

Хм а с каких пор анонимные переменные поддерживаются? Даже gcc 3.4 это скомпилировал.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от hateyoufeel

Ну замени на https://github.com/eyalroz/printf:

This is a small but fully-loaded implementation of C’s formatted printing family of functions. It was originally designed by Marco Paland, with the primary use case being in embedded systems - where these functions are unavailable, or when one needs to avoid the memory footprint of linking against a full-fledged libc. The library can be made even smaller by partially excluding some of the supported format specifiers during compilation. The library stands alone, with No external dependencies.

dataman ★★★★★
()
Ответ на: комментарий от utf8nowhere

Литерал это не переменная, это константа, ей можно кого-то инициализировать. А тут именно выделяется в стеке место под неё, туда можно записывать потом изменения, то есть это именно локальная переменная, но без имени.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Когда я делал подобную ерунду, у меня это как-то так выглядело:

LOG(a1, a2) -> log_start(); log_arg(a1); log_arg(a2); log_end();

log_arg это была _Generic функция, которая в зависимости от параметра вызывала log_arg_int и тд. Это, конечно, не совсем в тему, но вообще мне вполне хватало для моих небольших нужд, было удобно и типобезопасно. Для опций форматирования я делал доп-функцию, к примеру LOG(fmt_float(f, 2)), где функция fmt_float возвращала некую структуру struct fmt_float {float value; int precision; }, а функция log_arg имела перегрузку для этой структуры.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от EXL

Но лучше их не использовать т.к код она в режиме совместимости страшный генерирует и порой нерабочий. Более-менее нормально работать будет с c++20

mittorn ★★★★★
()
Ответ на: комментарий от vbr

А что касается попыток сделать printf типо-безопасным, по-мне это всё блажь и дурь. printf - он по определению смешивает compile-time и run-time. printf(argv[1], i1, i2) - давай, проверяй в рантайме, ага.

Compile safety должно начинаться с дизайна API. А printf по-мне просто идиотская идея изначально, хотя и снискала немалую популярность по непонятной мне причине…

vbr ★★★★★
()
Ответ на: комментарий от utf8nowhere

Посмотрел. Вобщем там неудачная терминология. Про compoud литералы я разумеется знал, но считал, что они строго rvalue и предназначены для инициализации или присваивания переменных сложных типов, вместо того чтобы каждое поле отдельно присваивать.

Называть литералом переменную (как по факту оказалось) - плохая идея.

firkax ★★★★★
()
Ответ на: комментарий от firkax

предназначены для инициализации или присваивания переменных сложных типов, вместо того чтобы каждое поле отдельно присваивать

Для этого { initializer-list } достаточно

Называть литералом переменную

Переменную — в смысле объект? Строковым литералам тоже объекты соответствуют.

utf8nowhere ★★★★
()

Мы уже sprintf осилить не можем? GCC даже Warning'и пишут, если неправильно написал printf.

В целом, задача решаемая во время компиляции в C++ путем адских шаблонов и макросов в С++98 или чуть менее адских в C++11. Но C, а тем более классический С это не умеет.

В Run-time можешь свой sprintf написать, но он будет работать хуже, потому что о его существовании не знает компилятор и ошибки он тебе не покажет и не оптимизирует где мог бы.

zx_gamer ★★★
()

зажрались. просто берется несколько функций, типа write_string, write_int, write_float,… и любой текст пишется ими. сишные принтфы - это собсно синтаксический сахар над всем этим, да еще и с парсингом форматной строки.

а раз оно у тебя без ос, то там и текстов будет - кот наплакал.

при правильном подходе это будет слегка длинней, зато все строго типизировано, без левых либ, и без возможного парсинга в рантайме.

alysnix ★★★
()
Ответ на: комментарий от hateyoufeel

Си так может?

спроси у godboltа, вроде может. раз он ругается на неформат, значит парсит строку при компиляции, а раз парсит - значит и в рантайме уже не надо.

просто во веки веков было, в языках, где нет принтфа с форматной строкой - просто печатали отдельными функциями и не заморачивались.

проверил на godbolt… нифига он не оптимизирует даже на -02…тююю. вот потому надо просто отдельными функциями и писать, еще и быстрей будет. https://godbolt.org/z/fc9dKG8Kq

а не… чета помнится, у него кажется флаг какой-то должен быть для этого дела. сам ищи короче.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 4)
Ответ на: комментарий от zx_gamer

Мы уже sprintf осилить не можем?

У нас тут кастомная либа под голое железо, доставшаяся в наследство, и там аналог printf() написан ещё до моего рождения (не шучу, там в комментах даты из 1990). Хочу от этого говна избавиться, потихоньку переводя код на использование чего-то нормального. Плюс printf() достаточно ублюдочная штука сам по себе, поэтому если его можно заменить, его нужно заменить. Если нет, выше кинули ссылку на вроде чуть более адекватную реализацию, буду смотреть туда.

GCC даже Warning’и пишут, если неправильно написал printf.

См. выше.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Меня пугает, что там 1500 строк, правда.

https://github.com/charlesnicholson/nanoprintf

1203 lines (1064 loc) · 39.9 KB

nanoprintf is an unencumbered implementation of snprintf and vsnprintf for embedded systems that, when fully enabled, aim for C11 standard compliance. The primary exceptions are scientific notation (%e, %g, %a), and locale conversions that require wcrtomb to exist. C23 binary integer output is optionally supported as per N2630. Safety extensions for snprintf and vsnprintf can be optionally configured to return trimmed or fully-empty strings on buffer overflow events.

Additionally, nanoprintf can be used to parse printf-style format strings to extract the various parameters and conversion specifiers, without doing any actual text formatting.

nanoprintf makes no memory allocations and uses less than 100 bytes of stack. It compiles to between ~580-2800 bytes of object code on a Cortex-M0 architecture, depending on configuration.

dataman ★★★★★
()

Библиотека для форматирования текста в Си

Использую свою функцию конкатенации значений переменных.
C printf код конечно короче.
Зато с использованием конкатенации код работает быстрее (нет парсинга форматной строки, … ).
Конкатенация у меня производится не с использованием «+», а функции конкатенации через запятую передаются соединяемые значения.

И хрустит и очень вкусно.

Ленюсь немножко переписать printf с использованием метаданных (использую динамические переменные).

anonymous
()
Ответ на: комментарий от anonymous

Ага, и компилируется за полнаносекунды. Свежо предание, да верится с трудом.

Именно так.
Да и выполняется быстрее.
Зачем процессор греть ГК?

API для работы с строками у меня своё и весьма эффективное.

anonymous
()
Ответ на: комментарий от anonymous

Скорее всего публиковать исходники не буду.
Это низкоуровневое core, которые будет использовать API для использования баз знаний.
А вот над этим core придётся крепенько подрудиться так как без
разработки высокоэффективных алгоритмов не решить эту задачу.

anonymous
()
Ответ на: комментарий от anonymous

Я тут операционку написал, заменяет линукс,виндовз и макос. Есть свой браузер и офисный пакет. Компиляторы, понятное дело, тоже свои. Все работает просто волшебно. Но исходники скорее всего публиковать не буду, ведь запускается это все великолепие на самодельном процессоре, сделанном при помощи штихеля и шлямбура.

anonymous
()
Ответ на: комментарий от anonymous

По секрету вас скажу, что printf конверитрует нативные значения переменных в строки, а затем формирует используя конкатенацию результирующую строку.
У меня преобразование нативных значений в строку производится с
помощью своего API.
Поэтому и сказал, что код prinf лаконичнее, а у меня много быстрее.

anonymous
()
Ответ на: комментарий от anonymous

Конкатенация у меня производится не с использованием «+», а функции конкатенации через запятую передаются соединяемые значения.

На крестах что ли пишешь? В сишке в принципе невозможно конкатенировать с помощью «+»

annulen ★★★★★
()
Ответ на: комментарий от hateyoufeel

аналог printf() написан ещё до моего рождения (не шучу, там в комментах даты из 1990)

Проблемы?

Хочу от этого говна избавиться,

А в чем проблемы?

потихоньку переводя код на использование чего-то нормального.

А критерии «нормального» будут?

Плюс printf() достаточно ублюдочная штука сам по себе,

А критерии «ублюдочности» будут?

zx_gamer ★★★
()

посыл «на чистом Си? Об printf() я слегка задолбался» говорит о том что что-то не так делается.

Си-шка должна просто плевать данные в базы и потоки как из пулемёта в машинно-читаемом виде (человеко-понятным это опция, а приятным на вид уже излишество).

А всякое красивое форматирование делается отдельными тулзами и шаблонизаторами. Тогда наступает счастье, все заняты именно своим делом, не ломают глаз об проценты, и не лезут в стабильный код на каждый писк менеджёрки.

MKuznetsov ★★★★★
()
Ответ на: комментарий от zx_gamer

аналог printf() написан ещё до моего рождения (не шучу, там в комментах даты из 1990)

Проблемы?

Ага. Проблемы. Эта штука не умеет %llX, как я вчера выяснил.

Хочу от этого говна избавиться,

А в чем проблемы?

См. выше.

потихоньку переводя код на использование чего-то нормального.

А критерии «нормального» будут?

Например, отсутствие необходимости следить за соответствием типа аргумента и строчки в форматной строке. Это должен делать компилятор. В данном случае, компилятор этого не делает. Вариант с {} в форматной строке куда более приятен и удобен.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Ага. Проблемы. Эта штука не умеет %llX, как я вчера выяснил.

То-то у тебя на логотипе заплаканное лицо.
А попробуй просто использовать вместо printf конкатенацию строк.
printf конечно более лаконичен, но зато у тебя код будет более быстро работать.
А ведь это не плохо …
Честно говоря строкой выше помещаю иногда комментарий, который показывает формат результирующей строки.
Впрочем у ста программистов сто разных суждений.

anonymous
()
Ответ на: комментарий от hateyoufeel

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

Iron_Bug ★★★★★
()