LINUX.ORG.RU

CEssentials

 ,


1

2

Я тут запилил небольшую библиотеку для Plain C:

https://github.com/KivApple/CEssentials

Что в себе содержит:

- Утилиты работы с динамическими строками

- Обобщённый вектор (на макросах)

- Обобщённая хеш-таблица (на макросах) с open adressing и quadratic probing

Обобщённые структуры данных реализованы типобезопасным образом (макросы порождают static inline функции под нужный тип данных, соответственно, у компилятора есть информация о типах).

Я открыт к критике как по поводу самого содержимого, так и его упаковки (насколько хорош подход, который выбрал именно я для реализации обобщённого программирования в C).

Библиотека распространяется под лицензией MIT и обладает исчерпывающей документацией.

UPD: Переписал в snake_case стиле, плюс перевёл полностью на макросы вместо порождения макросами кучи специализированных функций, потому что оказалось, что на практике глобальное пространство имён быстро засоряется, а других в plain C не завезли.

UPD2: Написал статью по устройству хеш-таблицы из библиотеки: https://habr.com/ru/post/704724/

★★★★★

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

я не настоящий сварщик, но разве вот эту дичь

CVec_TYPEDEF(IntVec, int)
CVec_IMPL(IntVec, int)
...
IntVec v;
IntVec_init(&v);
int a = 10, b = 20;
IntVec_push(&v, &a);
IntVec_push(&v, &b);
*IntVec_append(&v) = 30;
int sum = 0;
for (size_t i = 0; i < IntVec_size(&v); i++) {
    sum += *IntVec_at(&v, i);
}
printf("%i\n", sum); // Outputs "60"
IntVec_destroy(&v);

Нельзя это все оставить внутри и дать нормальный интерфейс?

Anoxemian ★★★★★
()

CStr_appendN
CStr_newFormatV
CStr_trimStartN

Извращенец :D На любителя, те кто пишет без автокомплитера взревут и будут психовать.

Обобщённый,Обобщённая,Обобщённые

Как истинный быдлокодер, спрошу, кто все эти люди? =)

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от ox55ff

Нет, там опечатка, пропущен первый аргумент функции. Это не скомпмлируется, так что утечки не будет. Исправил. Спасибо.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

А как назвать эти функции?

Если у каждой функции есть 3 версии: принимающая массив символов и длину, принимающая null terminated строку и принимающая CStr.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

Как истинный быдлокодер, спрошу, кто все эти люди? =)

Это значит, что алгоритм реализован один раз, но может быть применён для разных типов данных.

В плюсах для этого есть шаблоны, во всяких джавах - дженерики. Ну вот, у тебя есть динамический массив. Его алгоритм работы будет одинаков, что бы ты там не хранил - хоть числа, хоть указатели, хоть структуры - он будет содержать указатель на память, количество элементов и полный размер памяти (чтобы можно было добавлять-удалять элементы не переаллоцируя память каждый раз), будет определённая логика роста вектора и т. д.

В Си с этим на уровне языка немного туго. Можно уйти в полную динамику и хранить в векторе размер элемента (а в хеш таблице придётся хранить ещё указатели на функции хеширования и сравнения и делать косвенный вызов). Но это приводит к некоторой потере эффективности. Можно реализовать всё на чистых макросах, но тогда код вообще не будет переиспользоваться (даже если в разных местах выполняется вызывается одна и та же операция над одним и тем же типом данных), плюс не будет никакой типобезопасности (можно передать в макрос не те аргументы, которые хотел бы увидеть автор макроса, при этом ошибки/предупреждения компилятора будут малоинформативны). А можно сгенерировать объявления типов и функций макросами, а затем уже их использовать, я выбрал последний вариант.

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

Я не про названия.Просто мне вот так больше нравится

cstr_append_n(...)
cstr_new_format_v(...)
cstr_trim_start_n(...)

Это просто дело вкуса. КаМелКаСе подобное трудно перевариваю почмуто ^.^

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от KivApple

Это значит, что алгоритм реализован один раз, но может быть применён для разных типов данных.

Ааа, понял.

во всяких джавах - дженерики

У тебя c11 там есть генерики. Мы даже бодались с флаконом про это код генериков ниже там. Хотя подойдут ли они в твоей идее вопрос, надо смотреть код. А я леньтяй. Скорее всего нет, вернее сами генерики надо генерировать макросами ибо типы в них хардкорятся на этапе объявления генерика самого с функциями обработки типов. Это как бы генерики, но не генерики :D Ладно фик сними.

Спасибо за развёрнутый ответ.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)

Одобряю факт существования инициативы, но всё же:

1) cmake в зависимостях? это чтобы два раза запустить gcc и один - линкер (наверно тоже gcc)?

2) обычно все предпочиают писать такое сами (иногда даже несколько штук для разных проектов) чем пользоваться готовым, хотя может и пригодится

3) увы, но у тебя там и правда проблемы с переполнениями (в ряде мест), правда разумеется не там где 0x55ff указал; указывать места не буду, их поиск это вполне полезная задача для избежания таких ситуаций в будущем

firkax ★★★★★
()

Обобщённый вектор (на макросах)

Фигасе, я делал почти один-в-один то же самое, когда хотел запилить POC STL на C. Кажется, тогда я окончательно разочаровался в си (и дальше вектора не пошел), ибо нечитаемо оно в общем случае, и принципиально может не всё что есть в STL.

А для си, имхо, лучший вариант. Может, аллокаторы добавил бы. GNU-атрибуты функций помимо format(printf), опционально. И еще inline в *.c не имеет смысла.

unsigned ★★★★
()

Написал бенчмарк https://github.com/KivApple/CHashTable_benchmark. Алгоритм тот же как и для C++ и Rust - генерируем икосферу активно используя вектор и хештаблицу. Повторяем процесс 10 тысяч раз, замеряя общее время, а потом делим его на количество итераций. Между итерациями всё начинается с нуля (все контейнеры уничтожаются и создаются заново), однако в самом алгоритме применены все адекватные оптимизации (например, для всех контейнеров вызывается reserve вначале использования).

Версия для C++ использует flat_hash_map из библиотеки Abseil, версия для Rust AHashMap из одноимённой библиотеки, а версия для C использует мою реализацию.

Результаты:

[C++]
GCC: 67 us, 68 us
MSVC: 121 us, 118 us
Clang: 83 us, 87 us
[Rust]
62 us, 66 us
[C]
GCC: 47 us, 49 us
MSVC: 46 us, 46 us
Clang: 42 us, 40 us

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

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

1) CMake для удобства подключения в другие проекты на CMake. Я в своих проектах использую подход git submodule add + add_subdirectory.

А если почитать описание, то там указано, что по факту зависимостей у библиотеки и нет (кроме libc). Можно взять все или часть файлов из каталога src и подключить в свой проект напрямую. Библиотека не нуждая в конфигурировании и т. п.

Добавил уточнение, что CMake опционален.

2) Я обыскался нормальной библиотеки хештаблиц для Си. Нашёл Klib, в котором пулл реквесты висят с 2020 года, почти отсутствует документация, отстутствуют важные методы (например, у вектора нет clear, а resize не обновляет размер, а только ёмкость) и местами невнятный интерфейс (если считать макросы с двумя подчёркиваниями как деталь реализации, то публичный интерфейс позволяет определить прототипы функций хештаблицы, но они работать не будут, зато typedef отдельно от реализации сделать невозможно).

3) Можешь, пожалуйста, хотя бы дать наводку в каких компонентах есть проблемы? Сомневаюсь, что я сам найду.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

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

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от firkax
  1. обычно все предпочиают писать такое сами (иногда даже несколько штук для разных проектов) чем пользоваться готовым, хотя может и пригодится

Угу, я для одного своего недопроекта строки уже свелосипедил (в опенсорс пока не выкладываю, не потому, что жадный или ЧСВ, а потому, что лютый говнокод), надо будет сравнить с ТСовским.

указывать места не буду

Вот же ты какой :)

hobbit ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

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

KivApple ★★★★★
() автор топика