- PVSM.RU - https://www.pvsm.ru -
Если Вы программируете на Си и Вам не хватает типизированных контейнеров, которые есть в языках высокого уровня, добро пожаловать под кат:
Всем привет!
Как известно, нетривиальная программа с большой долей вероятности предполагает работу с нетривиальными структурами данных. Современные ЯП высокого уровня имеют обширные библиотеки типизированных контейнеров, несравненно упрощающих работу с ними. Однако, при падении с небес на землю на чистый Си все эти блага цивилизации оказываются недоступными. Поэтому зачастую при необходимости написать что-то на Си, возникает потребность в некоем коде, обеспечивающем обработку сложно структурированных данных.
Необходимость каждый раз заново изобретать велосипед с квадратными колесами привела мою измученную нарзаном избалованную ЯП высокого уровня душу к мысли о создании универсального генератора типизируемых структур (являющегося частью более обширного пакета AutoC [1]), способного решить эту проблему once and for all.
Существующие библиотеки контейнеров для Си, например из состава GLib [2], оперируют единственным типом — нетипизированными указателями gpointer, поскольку «честное» решение этой задачи на Си возможно только с привлечением препроцессора и будет представлять собой жуткую мешанину из макросов, трудную в понимании и отладке, что продемонстрировано существующими [3] подобными проектами. То есть, ничего подобного по стройности и удобству контейнеров STL для Си++ и коллекций Явы и Шарпа штатными средствами Си построить не получится.
В данной статье представляется альтернативный путь — автоматическая генерация требуемых структур данных по задаваемой пользователем спецификации. Полученные таким путем структуры данных, помимо собственно обеспечения строгой типизации элементов, очень наглядны и имеют простой и понятный интерфейс, что облегчает их использование.
Генератор кода написан на Руби. Подготавливаемая пользователем спецификация структур данных представляет собой Руби-скрипт. Результатом работы генератора является исходный код на Си, оформленный в виде модуля (файл заголовка и файл реализации). Файл заголовка включается в пользовательский код, использующий эти структуры; файл реализации компилируется и связывается с остальными модулями, составляющими программу.
На момент написания статьи поддерживаются следующие структуры данных:
Vector (Си++/Ява аналоги: std::vector и java.util.ArrayList).List (ближайшие Си++/Ява аналоги представляют собой двухсвязные списки: std::list и java.util.LinkedList).HashSet (Си++/Ява аналоги: std::unordered_set и java.util.HashSet).HashMap (Си++/Ява аналоги: std::unordered_map и java.util.HashMap).Для каждой специализации контейнера создается свой независимый исходный код; собственно, это именно то, что делает Си++ «за сценой» при генерации специализаций своих шаблонных типов (например, для std::list<int> и std::list<box> генерируются два независимых набора машинного кода).
Генерируемые структуры могут оперировать как типами, передаваемыми по значению, так и ссылочными типами. Для последних может быть задействовано контролируемое пользователем управление памятью, например, с совмещением имен и счетчиком ссылок, copy-on-write и т.д. Обеспечивается множество операций, ожидаемых от подобных структур данных — вставка, удаление, поиск, перебор элементов и т.д.
В качестве примера приведу простой пример работы с AutoC для создания и применения структуры данных, соответствующей типу std::unordered_set<int> для Си++.
Полный текст входного задания для генерации структуры данных типа набор (set), оперирующего целыми числами:
require "autoc"
CodeBuilder::CModule.generate!("containers") do |m|
m << DataStructBuilder::HashSet.new("IntSet", {:type=>"int"})
end
Вырезка из интерфейсной части модуля для IntSet:
...
typedef struct IntSet IntSet;
struct IntSet {...}
...
void IntSetCtor(IntSet*);
void IntSetDtor(IntSet*);
void IntSetPut(IntSet*, int);
...
Полный текст тестового примера для IntSet:
#include <stdio.h>
#include "containers_auto.h"
int main(int argc, char** argv) {
IntSet set;
IntSetCtor(&set);
IntSetPut(&set, 0);
IntSetPut(&set, 1);
IntSetPut(&set, 0);
printf("size(set)=%dn", IntSetSize(&set));
printf("contains(1)==%dn", IntSetContains(&set, 1));
IntSetDtor(&set);
return 0;
}
Работа над пакетом еще не завершена; основные пробелы находятся, как водится, в части документации (краткое описание генерируемых API в наличии). Тем не менее, содержательная часть уже довольно стабильна, функциональна и готова к использованию.
Более полное представление о возможностях и способах применения можно получить из тестового примера, входящего в состав пакета.
P.S.
Идеи и конструктивная критика приветствуются.
Автор: fougas
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ruby/28464
Ссылки в тексте:
[1] AutoC: http://autoc.sourceforge.net/
[2] GLib: http://developer.gnome.org/glib/
[3] существующими: http://sglib.sourceforge.net/
[4] Источник: http://habrahabr.ru/post/171339/
Нажмите здесь для печати.