Теперь подробнее.
Шаблоны — это вообще одна большая головная боль С++. Если для вас это не головная боль, то либо вы и ваши коллеги очень хорошо умеете ими пользоваться, либо вы и ваши коллеги (к счастью) вообще не умеете ими пользоваться, либо у вас все в порядке с чувством меры.
Вообщем, если при разработке библиотеки есть мысли о дальнейшем сращивании со скриптовым языком, лучше вообще забудьте про шаблоны. В скриптовых языках есть очень мощные средства полиморфизма, по сравнению с которыми templates нервно курят в сторонке. Геморроя, однако, будет создано немеряно.
Пример, библиотека для работы с Intersystems Caché на С++
d_ref «Article» article_ref;Вопрос. Если класс Article, описанный в базе, на этапе компилляции расширения (extension) неизвестен, каким классом инстанцировать шаблон? К счастью, библиотека на С таких проблем не ставит.
С++ делает попытку создать полиморфизм на этапе компилляции. Скриптовые языки обеспечивают полиморфизм на этапе выполнения. Это приводит к жутким последствиям.
Скриптовый язык без сборщика памяти — это нонсенс. Он есть во всех языках. Плохой ли, хороший ли, но он есть. Он даже в Objective C есть. Только в С++ его нет. Зато есть авто-указатели, реализованные через объекты на стеке.
Класс создается наследником от ReferenceCount, после чего при попадании в метод, происходит присваивание указателя на объект этого класса специальному объекту, созданному на стеке, который увеличивает счетчик ссылок на 1. Когда авто-указатель уничтожается при выходе из функции, на объекте счетчик ссылок уменьшается и объект высвобождается.
Поверьте, сборщик мусора в руби будет пищать от восторга, когда он будет работать с высвобожденной памятью.
Как и Objective C, руби основан на Smalltalk-е (потому они оба такие хорошие). Создание объектов разнесено на две операции: выделение и инициализация объекта. В руби это не видно, в Objective C это явно прописывается. При написании С-расширений к руби это видно очень хорошо.
Схема объявления объектов внутри руби такая, что надо заявить структуру, которая будет отвечать за объект.
struct rbDatabase { Database* db; };Работа с этой структурой происходит по указателю:
struct rbDatabase* db; … db→db→….Однако, очень не хочется работать через два указателя, так что хочется работать напрямую с Database. Но конструктор Database принимает обязательные параметры. Аллокация структуры происходит в одном месте, ее инициализация в другом. Параметры известны только при инициализации. Объяснить это С++-у нереально. Приходится извертываться.
Есть такая проблема: руби оформляет исключения, как longjmp. Если создать объект на стеке и кинуть в методе исключение, то деструктор объекта не выполнится. Мьютекс не отпустится, память не высвободится и т.д. и т.п.
Использование объектов на стеке с осмысленными деструкторами в методах, обеспечивающих работу с руби крайне не рекомендуется.
Если вы при аллокации структуры не укажете свою собственную функцию деаллокации, вам пиздец. Руби по умолчанию вызывает free на структуру. Если эта структура — ваш библиотечный класс, то С++ не простит вам вызова free на объект, созданный через new.
Если есть вариант отказаться от С+ — отказывайтесь. У вас есть руби, с которым не С++-у не равняться. Связь между ними будет на порядок более внятной, если библиотека на С.
Если от С++ не отказаться, и библиотеку можно править, то выкиньте шаблоны. Они вас похоронят. Уберите параметры конструктора, сделайте аксессоры.
Если используются объекты на стеке, постарайтесь не использовать наполненные логикой деструкторы. Они могут не вызываться.
Не используйте сборщики памяти в С++. Пользуйтесь для этого сборщиком памяти руби, либо очень, очень четко это разделяйте.
Не забудьте указывать свои функции деаллокации.