Пишу обвертку вокруг одной библиотеки, написанной на "сишечке".
- Пяток структур и полсотни функций в каждом хидере, документирована от силы четверть. Итого -- тысячи функций, которые вроде бы умеют все, но чтобы сделать что-то, на пол-шага отличающееся от примеров, приходится достаточно долго курить, а клиентский код получается весьма обширным. В результате -- за деревьями не видно леса.
- Отсутствие модификаторов доступа в С -- мне как клиенту библиотеки непонятно, какие данные я могу менять напрямую, а какие -- через функции библиотеки. Можно было бы спрятать приватные данные в pimpl, но автор до этого не додумался.
- Pure C реализации вектора/списка/хешмапы. Через void*, по другому ведь никак. Типобезопасность? Нет, не слышали. Хорошо, если комментарий есть -- "в этом списке char*, в этом -- struct foo*. В динамических языках хоть ассерт можно поставить, в С -- либо все ок, либо расстрел памяти.
- Вау, автор додумался до идеи конструктора/деструктора. Поэтому для каждой струкутры есть
struct lib_foo * lib_foo_new(/*параметры*/); void lib_foo_free(struct lib_foo*);
даже если этоstruct lib_foobar { int fb_foo; int fb_bar; };
Соответственно, все обьекты выделяются через malloc в хипе, в том числе и члены членов членов членов главной структуры. - А до виртуального деструктора автор не додумался (Или решил, что это оверхед). Поэтому при оборачивании обьекта в умный указатель приходится постоянно передавать ему делитер.
- Привычка добавлять сокращенное имя структуры как префикс поля (те самые fb_) в примере выше. В результате -- в клиентском коде будет куча копи-пасты вида
lib_foo_list * fill_foo(...) { lib_foo_list * foos = lib_foo_list_new(); fill(foos->foo_list, ...) return foos; } lib_bar_list * fill_bar(...) { libbar_list * bars = lib_bar_list_new(); fill(bars->bar_list, ...) return bars; } lib_baz_list * fill_baz(...) { libbaz_list * bazs = lib_baz_list_new(); fill(bazs->baz_list, ...); return bazs; }
которую не засунуть в шаблон. - Попытка сэкономить на копировании приводит к странному контракту функций - если функция завершилась успешно -- она владеет переданными обьектами. Если с ошибкой - то обьектами владеет клиентский код. Отсюда:
- В клиенской функции нужна секция обработки ошибок.
- Это не типичная для "сишечки"
clear_foo: lib_foo_free(foo); clear_bar: lib_bar_free(bar); return err;
а нечто позапутанней с дополнительными return-ами или goto-ми - Еще запутанней с функциями, принимающими char*. Одна функция копирует переданную строку, соседняя -- ее "овладевает". Комментариев обычно нет, смотри примеры или исходный код. И да, строки мы копируем через strdup, а длину меряем через strlen.
Комментариев нет:
Отправить комментарий