Программирование в ООП-стиле на Си как правило порождает достаточно большое количество boiler plate кода. А итоговая программа пестрит приведениям типов. «Хватит это терпеть» решил я и запилил сие поделие: https://github.com/KivApple/c-oop-gen.
1) Вы описываете структуру классов (поля, методы, наследование) в XML-формате.
2) Вы генерируете два заголовочных файла из этого описания. Рекомендуется делать это не в ручную, а в качестве одного из шагов компиляции проекта.
3) Первый файл вы инклюдите всюду, где хотите использовать описанные классы. Второй файл вы инклюдите только в модуль с реализацией соответствующих классов (там описаны таблицы виртуальных методов).
4) PROFIT
Пример описания пакета классов:
<?xml version="1.0" encoding="utf-8" ?>
<package>
<include file="stdlib.h"/>
<class name="BaseObject">
<method name="destroy" virtual="yes">
</method>
</class>
<class name="DerivedObject" inherits="BaseObject">
<field name="tmp" type="int"/>
<method name="foo">
<arg name="bar" type="int"/>
</method>
<method name="staticMethod" static="yes"/>
</class>
</package>
После этого методы классов можно вызывать легко и просто:
DerivedObject_foo(another_object, 10);
DerivedObject_staticMethod();
DerivedObject_destory(another_object);
BaseObject_destroy(some_object);
Как можно заметить, при наследовании класса от другого, создаются обёртки для всех методов, которые не были переопределены, что позволяет отказаться от ручного приведения типов.
Поддерживаются виртуальные методы (как абстрактные, так и имеющие реализацию, к слову, переопределять в потомках можно только виртуальные методы), статические методы (не наследуются потомком, рекомендуется конструкторы делать на них) и обычные. Также любые методы могут получить переменное число аргументов с помощью атрибута «variadic».
Конструктор может быть любой функцией. Главное присвоить obj->vtable = &ClassName_vtable, если класс содержит хотя бы один виртуальный метод.
Деструкторы - это обычные виртуальные функции.
Разумеется, чудес не бывает и ответственность за вызов родительских реализаций методов (особенно актуально для конструкторов и деструкторов) лежит полностью на вас. С другой стороны это предоставляет и большую гибкость.
В отличии от многих других библиотек привносящих ООП в Си, сгенерированный код не требует НИЧЕГО (даже libc). Ведь это просто набор define'ов и структур.
Кстати, пакеты классов могут использовать друг-друга с помощью <include package=«some-package.xml»>. В этом случае можно будет наследоваться от классов объявленных в другом пакете (а сгенерированный исходник будет содержать #include «some-package.h»). При этом однако же генерировать заголовочный файл для подключенного пакета придётся отдельно.
Также должен более-менее нормально работать автокомплит в вашей любимой IDE (разумеется, только после генерации заголовочных файлов), потому что имена у функций получаются более-менее понятные.
В общем, такие дела. Покритикуйте какие ещё возможности стоит добавить, а что, возможно, я сделал не так. Сообщения о багах также приветствуются. Безусловно, данный проект можно написать достаточно быстро, но я не нашёл аналогичные проекты.